Magento 2 Object Manager Preferences

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Today we’re going to explore the “class preference” feature of Magento 2’s object-manager/dependency-injection system. While this feature is a direct descendant of Magento 1’s class rewrite system, it ends up playing a different role in Magento 2. Where Magento 1’s class rewrites were aimed at allowing third party developers to customize system behavior, the class preference system is a core Magento 2 feature used to enforce a design by interface contract style of systems programming.

Installing the Sample Module

Like our other tutorials, we’ve prepared a module that sets up the basic commands and classes we use in this article. You can find the module on GitHub. As of this writing, the installation procedure for a Magento module isn’t 100% clear, so we recommend installing these tutorial modules manually using the latest tagged release. If you need help installing a Magento module manually, the first article in this series contains detailed instructions for doing so.

You’ll know you have the module installed correctly when you get the following output when running the ps:tutorial-object-preference command.

$ php bin/magento ps:tutorial-object-preference
Hello!

Assuming you can run the ps:tutorial-object-preference command, we’re ready to start.

Reviewing the Command Implementation

First, let’s review the implementation of the ps:tutorial-object-preference command in the following class file

#File: app/code/Pulsestorm/TutorialObjectPreference/Command/Testbed.php
<?php
namespace Pulsestorm\TutorialObjectPreference\Command;

use Magento\Framework\ObjectManagerInterface;
use Pulsestorm\TutorialObjectPreference\Model\Messenger;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Testbed extends Command
{
    protected $object_manager;
    protected $messenger;

    public function __construct(Messenger $messenger, ObjectManagerInterface $om)
    {
        $this->object_manager = $om;
        $this->messenger      = $messenger;

        return parent::__construct();
    }

    protected function configure()
    {
        $this->setName("ps:tutorial-object-preference");
        $this->setDescription("A command the programmer was too lazy to enter a description for.");
        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln(
            $this->messenger->getMessage()
        );  
    }
}        

If you’ve been following along with our series, nothing in this file should surprise you.

  • The main execute method calls the getMessage method of the object stored in the messenger object property.

  • The command code sets this messenger property in the constructor, and Magento creates the object itself via __construct dependency injection.

  • Re: the automatic dependency injection — the type hint of Messenger (full class name of Pulsestorm\TutorialObjectPreference\Model\Messenger) ensures the $messenger parameter contains an instantiated Pulsestorm\TutorialObjectPreference\Model\Messenger object.

If any of the above confused you, you may want to review the previous articles in this series.

Next, let’s take a look at the Pulsestorm\TutorialObjectPreference\Model\Messenger class definition file.

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Messenger.php 
<?php
namespace Pulsestorm\TutorialObjectPreference\Model;
class Messenger
{
    protected $message_holder;
    public function __construct(MessageHolderInterface $mhi)
    {
        $this->message_holder = $mhi;
    }

    public function getMessage()
    {
        return $this->message_holder->getHelloMessage();
    }
}

Here we see a similar pattern — the getMessage method calls the getHelloMessage method of the object in the message_holder property. The message_holder object contains an object that Magento creates via the __construct dependency injection system.

There is something different this time. The “object” that Magento injects is a Pulsestorm\TutorialObjectPreference\Model\MessageHolderInterface. Take a look at MessageHolderInterface’s definition file

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/MessageHolderInterface.php 
<?php
namespace Pulsestorm\TutorialObjectPreference\Model;
interface MessageHolderInterface
{
    public function getHelloMessage();
}

That’s no class, its a space station, PHP interface! If you’re not familiar with interfaces, we’ve written a short primer — but experienced PHP developers will know why we’re using exclamation points. You can’t instantiate a PHP interface — they’re not classes. So how is Magento injecting an interface?

Based on what we’ve learned about Magento’s object manager so far, you’re right to be confused. However, this is where the class preference feature comes into play.

Configuring Preferences for Classes

The first response to confusing code? Debugging with PHP’s reflection features, of course. Let’s try adding the following temporary var_dump debugging code to the Pulsestorm/TutorialObjectPreference/Model/Messenger’s constructor.

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Messenger.php 
<?php
    public function __construct(MessageHolderInterface $mhi)
    {
        var_dump(
            get_class($mhi)
        );
        exit;
        $this->message_holder = $mhi;
    }

Here we’re using PHP’s get_class function to view the object Magento injects. If we run our command with the above in place, we’ll see the following output.

$ php bin/magento ps:tutorial-object-preference
string(49) "Pulsestorm\TutorialObjectPreference\Model\English"

So, we know that Magento injects a Pulsestorm\TutorialObjectPreference\Model\English, and that there’s not some unholy voodoo that allows us to actually instantiate an interface, (phew!) However, this does raise another question

Why is the object manager (via dependency injection), instantiating a Pulsestorm\TutorialObjectPreference\Model\English object when the type hint is for a Pulsestorm\TutorialObjectPreference\Model\MessageHolderInterface?

This is what the class preference system is — it’s a way for end users to configure which classes Magento’s object manager should actually use when the object manager requests a certain class/type (or, in the context of automatic dependency injection, encounters a specific type hint).

In the next section we’ll cover how to configure a class preference, but don’t forget to remove the temporary var_dump and exit above before we move on.

Configuring Class Preferences

Most (if not all?) of Magento’s object manager features are configured through a module’s etc/di.xml file. For a Magento 1 developer coming in new to Magento 2, the new system continues the work that was going on in later versions of Magento 1, and splits out feature specific configuration information into their own configuration files.

If you take a look at di.xml, you’ll see the following “top level” (under the real top level <config/>, that is) node.

<!-- File: app/code/Pulsestorm/TutorialObjectPreference/etc/di.xml -->
<preference 
    for="Pulsestorm\TutorialObjectPreference\Model\MessageHolderInterface" 
    type="Pulsestorm\TutorialObjectPreference\Model\English" />

This is the configuration node where we can set a class preference. In plain english, we’re telling the object manager that

When someone asks you to instantiate a Pulsestorm\TutorialObjectPreference\Model\MessageHolderInterface (the for attribute) you should actually instantiate a Pulsestorm\TutorialObjectPreference\Model\English object (the type attribute)

If we take a look at the definition for Pulsestorm\TutorialObjectPreference\Model\English, we’ll see our simple Hello message

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/English.php    
//...
public function getHelloMessage()
{
    return 'Hello!';
}
//...

If you’re having trouble believing this, a quick example should set you straight. Let’s edit di.xml so it matches the following

<!-- File: app/code/Pulsestorm/TutorialObjectPreference/etc/di.xml -->
<preference 
    for="Pulsestorm\TutorialObjectPreference\Model\MessageHolderInterface" 
    type="Pulsestorm\TutorialObjectPreference\Model\Spanish" />

Here we’ve changed the type attribute to Pulsestorm\TutorialObjectPreference\Model\Spanish. This is a pre-defined class we included with the module. If we clear our Magento cache

$ php bin/magento cache:clean --all
Cleaned cache types:
config
layout
block_html
view_files_fallback
view_files_preprocessing
collections
db_ddl
eav
full_page
translate
config_integration
config_integration_api
config_webservice

and re-run our command, we should see the following changed output

$ php bin/magento ps:tutorial-object-preference
Hola

The Hola text comes from our newly configured class

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Spanish.php
public function getHelloMessage()
{
    return 'Hola';
}        

By changing di.xml, we told the object manager to inject a different class/object. While this example is (obviously) simplified for pedagogical reasons, this is a powerful feature you can use to change the behavior of Magento 2 system wide.

Not Just Interfaces

Class preference configuration is not just for interfaces — you can change actual classes as well. Let’s jump back to our command definition file

#File: app/code/Pulsestorm/TutorialObjectPreference/Command/Testbed.php
use Pulsestorm\TutorialObjectPreference\Model\Messenger;
//...
public function __construct(Messenger $messenger, ObjectManagerInterface $om)
{
    $this->object_manager = $om;
    $this->messenger      = $messenger;

    return parent::__construct();
}

You’ll recall Magento instantiates the Pulsestorm\TutorialObjectPreference\Model\Messenger object via automatic constructor dependency injection. Even though this is a concrete class, we can still change it. Try adding the following new <preference/> node to di.xml

<!-- File: app/code/Pulsestorm/TutorialObjectPreference/etc/di.xml -->
<config>
    <!-- ... -->
    <preference 
        for="Pulsestorm\TutorialObjectPreference\Model\Messenger" 
        type="Pulsestorm\TutorialObjectPreference\Model\Messenger2" />
    <!-- ... -->
</config>

Here we’ve told Magento that anytime someone requests that the object manager instantiate a Pulsestorm\TutorialObjectPreference\Model\Messenger object, that the object manager should actually instantiate a Pulsestorm\TutorialObjectPreference\Model\Messenger2 object.

If you add the above node to di.xml, clear your cache, and re-run the program, you should see the following output

$ php bin/magento ps:tutorial-object-preference
Injection?  We don't need no stinking injection!

That less than friendly message comes directly from our newly configured Pulsestorm\TutorialObjectPreference\Model\Messenger2 class.

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Messenger2.php
<?php
namespace Pulsestorm\TutorialObjectPreference\Model;
class Messenger2 extends Messenger
{    
    public function getMessage()
    {
        return 'Injection?  We don\'t need no stinking injection!';
    }
}

This, in effect, makes the class preference system the successor to Magento 1’s class rewrite system.

Enforcing “Design by Contract”

While the class preference system is similar to Magento 1’s class rewrite system, it’s superior in at least one way. Let’s run through one last example.

Consider the following Pulsestorm\TutorialObjectPreference\Model\Messenger3 class

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Messenger3.php
<?php
namespace Pulsestorm\TutorialObjectPreference\Model;
class Messenger3
{    
    public function getMessage()
    {
        return 'Injection?  We don\'t need no stinking injection!';
    }
}

This class is very similar to our Pulsestorm\TutorialObjectPreference\Model\Messsage2 class, with one exception: It doesn’t extend the original Pulsestorm\TutorialObjectPreference\Model\Message class. Let’s see what happens when we configure this class in di.xml (replacing the Message2 class)

<!-- File: app/code/Pulsestorm/TutorialObjectPreference/etc/di.xml -->
<config>
    <!-- ... -->
    <preference 
        for="Pulsestorm\TutorialObjectPreference\Model\Messenger" 
        type="Pulsestorm\TutorialObjectPreference\Model\Messsage3" />
    <!-- ... -->
</config>

Clear your cache and run the command —

$ php bin/magento ps:tutorial-object-preference
Autoload error: Argument 1 passed to
Pulsestorm\TutorialObjectPreference\Command\Testbed::__construct() 
must be an instance of Pulsestorm\TutorialObjectPreference\Model\Messenger
instance of Pulsestorm\TutorialObjectPreference\Model\Messenger3 given,
called in
lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php   
on line 99 and defined ...

Curses — an error! However, this is a helpful error. What happened here was we successfully configured Messenger3 — but when Magento tried to inject it into the __construct method

#File: app/code/Pulsestorm/TutorialObjectPreference/Model/Messenger3.php

public function __construct(Messenger $messenger, ObjectManagerInterface $om)

this new class failed the type hint check, and PHP rejected the change. While this may seem annoying, it’s actually a good thing. The class preference system in Magento 2 has an added layer of type safety. In Magento 1 you could replace a class alias like Mage::getMode(catalog/product) with any PHP class you wanted — but if that class was missing a method, your project would fail, sometimes in mysterious “not immediately evident” ways.

Similarly, our previous English and Spanish classes each implemented the MessageHolderInterface

namespace Pulsestorm\TutorialObjectPreference\Model;
class English implements MessageHolderInterface

namespace Pulsestorm\TutorialObjectPreference\Model;
class Spanish implements MessageHolderInterface

By using PHP’s built-in type hints for automatic dependency injection, Magento gets this added level of type safety for free. By forcing developers along a path where they’re required to use dependency injection to instantiate objects, Magento forces developers along this type-safe (or type-less-dangerous) path.

Once you’ve digested all this, you may be left with a lingering question

Is it better to use interfaces for all my injected dependencies, or should some be concrete class files?

While that’s a very good question, it’s a question without (at the moment) a clear answer. I will say, all other things being equal, it would be better to have an interface for any dependencies you’re injecting into the system, as an interface allows future developers more flexibility in building a replacement class for your dependency. Of course all things are never equal, and the true best practice will reveal itself after Magento 2 starts getting some real world use.

To Rewrite or Not Rewrite

While we’ve described the object manager’s class preference system as a replacement for the class rewrite system — and you can certainly use it as such — its real value comes from being a tool for developers interested in using design by contract style programming in their modules. By letting developers specify dependency injectable type hints that are interfaces, developers are (in turn) encouraged to use interfaces — knowing they can swap out their concrete implementations if they need to with a simple configuration change (as opposed to a complex refactoring).

There’s also another reason to look askance at the class preference feature as a replacement for class rewrites — and that’s because Magento’s object manager comes with a slew of other features that are both more powerful, and more specific, than the broad rewrite concept. We’ll explore more of these features in our next article, when we cover the configurable arguments feature of Magento 2’s object manager system.

Originally published July 22, 2015

PHP Primer: Interfaces

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

On a “what they do level”, interfaces are pretty simple. Consider the following PHP class

<?php

class Foo extends Bar
{   
}

We’re all (I hope!) familiar with the idea of one class extending another. Interfaces allows a PHP class to do something else. With interfaces, a PHP class may also implement something

<?php

class Foo extends Bar implements BazInterface
{   
}

In the above code sample, the class Foo implements the interface named BazInterface.

As to what it means to implement an interface, consider this simple program

#File: interface-example.php   
<?php

interface AlohaInterface
{
}

class Hello implements AlohaInterface
{
    public function sayHello()
    {
        echo 'Hello',"\n";
    }
}

$cmd = new Hello;            
$cmd->sayHello();

You’ll notice in this program we’ve defined an interface named AlohaInterface (interfaces don’t need the word Interface in their name, it’s just a common convention), and our class Hello implements this interface. If we run this program from the command line or a browser, we’ll see the following output

$ php interface-example.php 
Hello

So far, the interface hasn’t had any measurable impact on our program. However, if we change the interface

#File: interface-example.php  
//... 
interface AlohaInterface
{
    public function sayHello();
    public function sayGoodbye();
}
//...

and try running our program again, we’ll get the following error

PHP Fatal error:  Class Hello contains 1 abstract method and must therefore 
be declared abstract or implement the remaining methods 
(AlohaInterface::sayGoodbye) in 
interface-example.php on line 15

If this is gobbledygook to you, don’t worry, it’s gobbledygook to everyone at first. The key to understanding these sorts of error messages is to understand what an interface is for.

What is an Interface

Think of an interface like a stencil for your class. When you implement an interface, it’s sort of like telling PHP

Hey, I’m making this sort of class, so make sure I actually do the job right

When you define an interface, you’re making the stencil for your class. When you said

#File: interface-example.php  
//... 
interface AlohaInterface
{
    public function sayHello();
    public function sayGoodbye();
}
//...

You were telling PHP that any class that implements the AlohaInterface must define both a sayHello method, and a sayGoodbye method. You’ll notice the methods above don’t have any body. That’s because they’re not real methods, they’re just us telling PHP what a class should look like. Because they’re “not real”, we call these abstract methods.

So, going back to our error message

PHP Fatal error:  Class Hello contains 1 abstract method and must therefore 
be declared abstract or implement the remaining methods 
(AlohaInterface::sayGoodbye) in 
interface-example.php on line 15

PHP was telling us that, because our Class Hello has an interface with an abstract method, that we need to define (the error message uses the word implement) the abstract method (AlohaInterface::sayGoodbye).

So lets define it!

#File: interface-example.php   
//...
class Hello implements AlohaInterface
{
    public function sayHello()
    {
        echo 'Hello',"\n";
    }

    public function sayGoodbye()
    {
        echo 'Goodbye',"\n";
    }
}
//...

Try running the program with the above class definition in place, and everything should be back to normal.

$ php interface-example.php 
Hello

That’s all an interface is — it’s a way to ensure a class has a certain number of methods. As usual, this is a quick primer: The PHP manual has the full story on interfaces, as well as their close cousins, abstract classes.

Why Interfaces

We started this article saying that the “what they do” of interfaces was relatively simple. The “why” of interfaces is a harder story to tell. If you’re coming from shell scripting, or even certain sorts of application development, you’re probably wracking your brain for a reason to use interfaces.

Hint: The reason you’re having so much trouble thinking of a reason is — there isn’t one.

Interfaces only make sense if you’re building programmatic systems for other people to use. They allow a system developer to specify exactly how certain classes need to behave. Magento 1 contained numerous examples of this. Consider the shipping carrier classes

class Mage_Shipping_Model_Carrier_Flatrate
    extends Mage_Shipping_Model_Carrier_Abstract
    implements Mage_Shipping_Model_Carrier_Interface

Whenever you wanted to implement a new shipping carrier in Magento 1, you’d start with a class that implemented the Mage_Shipping_Model_Carrier_Interface. This forced you to define the same methods that the other shipping carrier classes had. This, in turn, meant the Magento systems code for fetching carrier rates could safely assume your carrier object had the same methods as the other carrier objects.

Interfaces are a way for programmers to communicate indirectly. By looking at the interfaces defined in a system, you’ll start to get an idea of what you’ll need to do to customize a system, without needing to talk to the original developer or going through a tutorial.

Java Legacy

Interfaces are another one of those features we get from Java, and that play a slightly different role in a dynamic language like PHP. That said, I think they’re one of the features that makes sense in PHP and can help avoid a certain class of bugs.

Consider the following pseudo-code

function someFunction ($object, $flag)
{
    $object->doTheThing();
    if($flag)
    {
        $object->doTheOtherThing();
    }    
}

In the above code, if $object doesn’t implement a doTheThing method — calling someFunction will always fail. However, if $object does implement doTheThing but doesn’t implement doTheOtherThing, the program might not fail, depending on the value of $flag. This mean it’s possible that a developer will ship this code to production with a lurking bug. You might say tests could catch this, and you’d be right, but I could also say there are large swaths of the industry where tests don’t catch this. This is where interfaces, combined with PHP type hints, can save us

interface SomeInterface
{
    public function doTheThing();
    public function doTheOtherThing();
}

@highlightsyntax@php 
function someFunction (SomeInterface $object, $flag)
{
    $object->doTheThing();
    if($flag)
    {
        $object->doTheOtherThing();
    }    
}       

By using an interface as a type hint, we can ensure no one calls someFunction with an object that doesn’t have all the needed methods.

Regardless of what you think of interfaces and programming by contract, they’re part of the PHP world now. Knowing how to read code that uses interfaces will be a vital skill for anyone working with modern PHP systems.

Originally published July 18, 2015

Magento 2’s Automatic Dependency Injection

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

This week’s article is the second in a series discussing Magento 2’s object system. Last time we covered the basics of Magento’s object manager. This week we’re going explain why you’ll rarely use Magento’s object manager, as well as Magento’s implementation of the popular “dependency injection” pattern. (hint — they’re related)

Many popular PHP frameworks implement a dependency injection system — although it would be more accurate to say they implement an automatic dependency injection system. Before we dive into Magento’s system, we’ll want to explain the problem dependency injection sets out to solve.

Understanding Dependency Injection

The easiest way to understand dependency injection is by example. Consider the following PHP method/pseudo-code

//Obviously contrived -- if only getting a price were this simple
public function getFormattedPrice($sku)
{
    $db  = new DBHandler;
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);

    $formatter = new PriceFormatter;

    return $formatter->asDollars($row['price']);
}

This is a simplified example of what a method for fetching a product’s price might look like. On the surface, there’s nothing wrong with this method. It

  • Instantiates a database handler

  • Queries for the price

  • Instantiates a formatter object

  • Uses the formatter object to return a formatted price

The problem comes later, when someone else wants to reuse this method. This method is now dependent on the specific DBHandler class, and the specific PriceFormatter class. Even if you think code reuse is an untrue industry myth, these two dependencies make this method harder to test in an automated framework. Your test framework is now reliant on making the same database connection as your real application.

The solution to this problems is to not have methods with these sorts of dependencies. Instead, you should inject dependencies into the method.

public function getFormattedPrice($sku, $db, $formatter)
{
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);
    return $formatter->asDollars($row['price']);
}

Rewritten, this method has two new parameters. The idea is the client programmer should pass in instantiated objects (i.e. inject the dependencies).

That’s all dependency injection is — the wikipedia entry has more examples of the concept and is worth a read, if you can stomach the java examples!

Modern Dependency Injection

Dependency injection is a pretty simple concept, but if you’ve never encountered it before there may be some doubts scratching at the back of your head. One of them may be this

public function prepareDataForDisplay()
{
    //...

    $data           = new stdClass;
    $db             = new DBHandler;
    $formatter      = new PriceFormatter;
    $data['price']  = $this->getFormattedPrice($row['price']);

    //...
}

public function getFormattedPrice($sku, $db, $formatter)
{
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);
    return $formatter->asDollars($row['price']);
}

While we’ve replaced the dependencies in getFormattedPrice — all we’ve really done is shift them up a level to the method that calls getFormattedPrice (prepareDataForDisplay above). So, conceptually dependency injection is simple, but where you inject your dependencies and where these objects are instantiated is left up in the air.

This is the problem that many PHP frameworks try to solve with some sort of automatic dependency injection. By creating a system for automatically injecting these sorts of dependencies the framework removes the question of by who and how a dependency is injected. If that didn’t make sense, don’t worry. After looking at an example of Magento’s dependency injection it should start to make more sense.

Magento Dependency Injection

Just like we did last time, we’ve prepared a module with some sample code. The module’s on GitHub, and the easiest way to install it is to manually download the latest release.

If you need help manually installing a Magento extension our previous series article has complete instructions.

Let’s make sure you have the module installed correctly by running the following command

$ php bin/magento ps:tutorial-object-manager-2
Hello Again World!

If you see the “Hello Again World!” message, you’re good to go.

If we take a look at the class that implements our command, we see the following .

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/Testbed.php
protected function execute(InputInterface $input, OutputInterface $output)
{
    $manager = $this->getObjectManager();
    $helper = $this->getObjectManager()->create(
        '\Pulsestorm\TutorialObjectManager2\Model\Example');
    $output->writeln(
        $helper->sendHelloAgainMessage()
    );        
}

All we’ve done in the execute method is instantiate a Pulsestorm\TutorialObjectManager2\Model\Example object and call its sendHelloAgainMessage method to get some output text. If you take a look at this Pulsestorm\TutorialObjectManager2\Model\Example class’s constructor

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php    
public function __construct()
{
    $object = new Message; //
    $this->messageObject = $object;
}

we see the class instantiates a Pulsestorm\TutorialObjectManager2\Model\Message object and assigned it to the messageObject property. We use the short class name Message since this file lives in the Pulsestorm\TutorialObjectManager2\Model namespace. If you need some help getting started with PHP namespaces, our primer is a good place to go.

Then, in the sendHelloAgainMethod

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
public function sendHelloAgain()
{
    return $this->messageObject->getMessage();
}

we use the Message object to return a text message.

Our (contrived) Pulsestorm\TutorialObjectManager2\Model\Example class has a hard coded dependency on the Pulsestorm\TutorialObjectManager2\Model\Message object. Here’s how Magento 2 solves this problem. Open up the Example.php class definition file, and let’s replace the constructor with the following code

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
public function __construct(Message $message)
{
    $this->messageObject = $message;
}

What we’ve done here is added a parameter named $message to the constructor, and then assigned that parameter to the messageObject property. In other words, we’ve replaced the hard coded dependency with a parameter. This allows developers to inject the dependency. You’ll also notice we’ve included a type hint with the parameter.

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
__construct(Message $message);

This type hint forces developers to pass in an object that either is a Pulsestorm\TutorialObjectManager2\Model\Message object, or has Pulsestorm\TutorialObjectManager2\Model\Message in its ancestry chain. Again, we’ve used the short class name Message. The following would have been equivalent, but is much more verbose.

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
__construct(\Pulsestorm\TutorialObjectManager2\Model\Message $message);

If you’re not familiar with type hints, we’ve prepared a primer on them.

If this were a traditional PHP application, we’d inject the dependency with code that looks like this

$dependency = new \Pulsestorm\TutorialObjectManager2\Model\Message;
$helper     = new \Pulsestorm\TutorialObjectManager2\Model\Example($dependency);

However, with Magento 2’s object system, we don’t get to supply arguments

$helper = $this->getObjectManager()->create('Pulsestorm\TutorialObjectManager2\Model\Example');

So what are we supposed to do?

That’s the beauty of Magento 2’s automatic dependency injection system. You don’t need to do anything. Just run the command with our new code in place.

$ php bin/magento ps:tutorial-object-manager-2
Hello Again World!

Same results — even though we didn’t do anything to pass in a parameter.

This is automatic dependency injection. Behind the scenes, the object manager will use PHP’s reflection features to look at a class’s __construct type hints/parameters, automatically instantiate the object for us, and then pass it into the constructor as an argument.

That can seem a little weird the first time you encounter it, but if you don’t believe us just add a bit of debugging code to your constructor

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php    
public function __construct(Message $message)
{
    var_dump(get_class($message));
    exit;
    $this->messageObject = $message;
}

re-run the command, and you’ll see the class name printed out.

$ php bin/magento ps:tutorial-object-manager-2
string(47) "Pulsestorm\TutorialObjectManager2\Model\Message"

Once you’re over the weirdness, you may wonder why this is any better. Isn’t the type hint just the hard coded dependency now?

The difference is the object manager has control over the instantiation of the dependency. As module developers, we get a great deal of power to change how the object manager instantiates an injected dependency. This happens via the module’s etc/di.xml file (the di stands for dependency injection). While Magento 2 doesn’t have “class rewrites” — it does have similar, more powerful features, all configurable via the di.xml file. Over the next few articles we’re going to explore all these options, and you’ll learn how to gain complete control over these dependencies.

So Long Object Manager, We Hardly Knew You

Before we wrap up for today, there’s one last thing to attend to. If you poke around Magento 2’s current documentation for Magento 2’s object system (usually labeled Dependency Injection), you’ll see cryptic comments like the following

The object manager must be present only when composing code, composing code is performed early in the bootstrapping process

Also, if you’ve been following Magento 2 development closely, you’ll know there used to be a static factory method for grabbing an object manager instance, but that method was depreciated and removed.

The object manager class is not meant for day-to-day use in Magento 2. It’s reserved (by convention) for system level developers working on the code that bootstraps Magento. The getObjectManager method we’ve provided in these tutorials was a helper so you could understand what the object manager was.

If you’re following best practices for Magento extension development, you’ll use dependency injection to instantiate almost all your objects. This may seem impossible at first (I was rolling my eyes back in 2013), but as Magento 2 nears completion it’s becoming clearer that this actually is possible.

We’ll be covering the dependency injection features that make this possible in our future articles. However, here’s some high level reassurances that “no objet manager” isn’t as crazy as it sounds.

  • All objects in Magento are instantiated with the object manager — including dependencies. In other words, the objects you inject with __construct injection can, themselves, have a __construct method with automatically injected dependencies.

  • Data management objects (what we used to call “ORM Models” in simpler times) can be instantiated via factory objects — these factory objects can be injected via dependency injection.

  • If it’s dependency injection all the way down, you may wonder what’s on the top of the stack. Remember though, all valid code entry points (a controller action method, an observer object/method, a block object, a Symfony Console command, etc.) are created by configuration. This means Magento knows about them, and when it needs to instantiate them it uses the object manager. This means all these objects have __construct injection

  • Although it’s not recommended, and may go away in a future release, as of right now there is a way to get an instance of the object manager

Regarding that last item? Take a look at the base Pulsestorm\TutorialObjectManager2\Command\AbstractCommand class

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/AbstractCommand.php
use \Magento\Framework\ObjectManagerInterface;

//...

public function __construct(ObjectManagerInterface $manager)
{
    $this->objectManager = $manager;
    parent::__construct();
}

protected function getObjectManager()
{
    return $this->objectManager;
}

This is the base class for our tutorial module’s cli classes, and where getObjectManager is implemented. How did we get an instance of the object manager? By using dependency injection, of course!

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/AbstractCommand.php    
public function __construct(ObjectManagerInterface $manager)

This pattern can take a little getting used to, and may seem like overkill. However, in an enterprise environment consistency is more important than a home run. Constricting day-to-day development along this path and leaving more creative work to the deep level systems developers, an agile focused team can do a better job of hitting its deadlines and calculating its velocity. Boring and corporate as they are, these design patterns are an important part of ensuring Magento 2’s steady, relentless march towards a release.

That’s a lot to digest for a single day, so we’ll leave it there. Next time we’ll explore some of the dependency injection features, including how we managed to inject an object manager using only a PHP interface (ObjectManagerInterface) instead of a class.

Originally published July 13, 2015