Argument injection is done via preference and type definitions within the di.xml.
By performing a lookup for the <preference string across the entire <MAGENTO_DIR> directory's di.xml files, we can see that Magento uses hundreds of preference definitions, spread across the majority of its modules.
Let's take a quick look at one of the __construct method, of the type Magento\Eav\Model\Attribute\Data\AbstractData:
public function __construct(
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Locale\ResolverInterface $localeResolver
) {
$this->_localeDate = $localeDate;
$this->_logger = $logger;
$this->_localeResolver = $localeResolver;
}
We can find the preference definitions for these interfaces under the <MAGENTO_DIR>/magento2-base/app/etc/di.xml file:
<preference for="Magento\Framework\Stdlib\DateTime\TimezoneInterface" type="Magento\Framework\Stdlib\DateTime\Timezone" />
<preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\Monolog" />
<preference for="Magento\Framework\Locale\ResolverInterface" type="Magento\Framework\Locale\Resolver" />
Theoretically, we can use the object manager directly, as follows:
class Type {
protected $objectManager;
public function __construct(
\Magento\Framework\ObjectManagerInterface $objectManager
) {
$this->objectManager = $objectManager;
}
public function example() {
$this->objectManager->create(\Fully\Qualified\Class\Name::class);
$this->objectManager->get(\Fully\Qualified\Class\Name::class);
\Magento\Framework\App\ObjectManager::getInstance()
->create(\Fully\Qualified\Class\Name::class);
\Magento\Framework\App\ObjectManager::getInstance()
->get(\Fully\Qualified\Class\Name::class);
}
}
The direct use of the objectManager is highly discouraged, as it prevents type validation and type hinting that a factory class provides.
By doing a lookup for the <type string across the entire <MAGENTO_DIR> directory's di.xml files, we can see that Magento uses over a thousand type definitions, spread across the majority of its modules.
Here is a very simple example, taken from the <MAGENTO_DIR>/module-customer/etc/di.xml file:
<type name="Magento\Customer\Model\Visitor">
<arguments>
<argument name="ignoredUserAgents" xsi:type="array">
<item name="google1" xsi:type="string">Googlebot/1.0 ([email protected] http://googlebot.com/)</item>
<item name="google2" xsi:type="string">Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)</item>
<item name="google3" xsi:type="string">Googlebot/2.1 (+http://www.googlebot.com/bot.html)</item>
</argument>
</arguments>
</type>
Looking into the source of the Magento\Customer\Model\Visitor class, we can see that it has its constructor defined by the $ignoredUserAgents = [] array. Using the type element, the preceding example injects the ignoredUserAgents argument with the given array values.
When configuration files for a given scope get merged, array arguments with the same name get merged into a new array. However, if any new configuration is loaded at a later time, either by a more specific scope or through the code, then any array definitions in the new configuration will replace the loaded configuration instead of merging.
The list of available item type values goes well beyond just a string, and includes the following:
- boolean
- string
- number
- null
- object
- const
- init_parameter
- array
See <MAGENTO_DIR>/framework/Data/etc/argument/types.xsd and <MAGENTO_DIR>/framework/ObjectManager/etc/config.xsd for specific type definitions.
Argument injection often goes hand in hand with virtual types, as we will soon see.