1

I am going to focus on Zend Framework, becasue I know it best, but this is certainly applicable to other similar mechanisms found in other PHP Frameworks that use an Inverstion of Control container.

In case of Zend Framework, with a bit of refactoring/manipulation it is entirely possible to not use a container.

Working Example

Looking at the docs, currently Zend Framework is doing something like this with $container (taken from here):

function getServiceConfig()
{
    return [
        'factories' => [
            Model\AlbumTable::class => function ($container)
            {
                $tableGateway = $container->get(Model\AlbumTableGateway::class);
                return new Model\AlbumTable($tableGateway);
            },
            Model\AlbumTableGateway::class => function ($container)
            {
                $dbAdapter = $container->get(AdapterInterface::class);
                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(new Model\Album());
                return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
            }
        ]
    ];
}

//to call
$albumTable = $container->get(Model\AlbumTable::class)

The left side of => contains "keys", which could be any string including a class definition string. The right side is a callback or a FQDN for class name. In the above example it's an anonymous PHP function that acts like a factory to instantiate a new entity, using a DiC $container that's passed to that function via a Framework call.

Why not remove the container and do something like this:

//define
function AlbumTableFactory()
{
    $tableGateway = AlbumTableGatewayFactory();
    return new Model\AlbumTable($tableGateway);
}

function AlbumTableGatewayFactory()
{
    $dbAdapter = AdapterInterface();
    $resultSetPrototype = new ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(new Model\Album());
    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
}

//to call
$albumTable = AlbumTableFactory();

Above could be converted to classes, if bare functions are bothersome.

In the above I removed the $container and replaced any container use with hardcoded new function calls and since there were no supplied parameters

Since the factories array is part of configuration of the Zend's container, to truly factor it out I'd have to replace any other instances of container calling the Model\AlbumTable::class with a factory call, in order to return the Model\AlbumTable instance. Once it's done I can have a container-less Zend implementation to where I call factories directly instead of calling them through a container with all dependencies explicitly defined.

The question spirit is .. why use a container, what exactly does it provide, and why not just use "real PHP code" with the benefit that no dependencies will be hidden via a container.

Dennis
  • 7,907
  • 11
  • 65
  • 115
  • 1
    Let's move Zend Framework out of the equation here. Why use dependency injection? Because we are lazy. Because it's easier to get objects that depend on other objects via DI. There's no need to remember every single class it depends on. Make it someone else's problem. When you ask for the object from DI, you get the created thingy you can work with. That's the short reason. Sure, you can use `new MyClass(new Other, new SomethingElse, $my_array_with_stuff, $some_other_array, $some_boolean_var`). Multiply by more classes / features you work with and this quickly gets out of hand. – N.B. Jul 18 '17 at 21:18
  • 1
    Having wrote the above, what tends to happen is that people use DI for _every_single_thing_ and that plainly sucks. There's no magic bullet. DI isn't always the answer. Sometimes things are simple and you can do simply `$myObj = new MyClass(['config' => 'option']);`. There's no rule saying you are bad if you don't DI everything. Right tool for the right job applies always. Sadly, common sense isn't as common as it used to be. – N.B. Jul 18 '17 at 21:23
  • Let's split the discussion in "DI" and "DI Containers". DI is a _practice_, and a rather good one that allows you to conform to the SOLID principles. A DI Container is a _tool_ that might help you simplify your Composition Root, but whether or not a DI Container is helpful depends on a lot of factors (such as used language, application size, application design, and structure of the application object graphs). You can practice DI without using a DI Container. This practice is typically referred to as [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di/). – Steven Jul 19 '17 at 06:47
  • it looks like Zend Framework picked DI container as a tool to avoid the burden of hardcoding any initialization parameters. It injects container into functions, where each dependency is pulled from the container, without worrying or thinking about "which parameter does my object need injected?" Using DI Container allows you to *break* Pure DI practices with the benefit of not concerning yourself with "what do I pass to this particular object so that it can be created?" You just pass that object a container and object itself asks for what it needs. Another benefit I see is caching an instance – Dennis Jul 19 '17 at 14:20
  • perhaps another benefit is avoiding leaving functions in the global scope. Even though those functions can be written into a class and be autoloaded instead, DiC allows you to use functions without polluting global scope. – Dennis Jul 19 '17 at 14:27
  • I am not entirely convinced that using Di Container *in this case* is necessarily "better" as far as anything significant is concerned. I can only see centralizing the caching mechanism to the container rather than writing one into every call as the only reasonable benefit. Ability to use anonymous function doesn't really strike me as a benefit, more like a side-effect – Dennis Jul 19 '17 at 14:36

0 Answers0