dependency-injectionphpunitmagic-methodsphp-5.5

PHP Dependency Injection - magic methods injections?


I'm trying to get my head around DI. Am I doing it correctly for classes that follow DI pattern?

class Boo 
{
    public $title = 'Mr';
    public $name = 'John';
    protected $deps;

    public function __construct($deps)
    {
        $this->deps = $deps;
    }

    public function methodBoo()
    {
        return 'Boo method '.$this->deps;
    }
}

class Foo 
{
    private $objects;

    public function __construct()
    {
    }

    // Set the inaccessible property magically.
    public function __set($name, $value)
    {
        $this->$name = $value;
    }

    // Set the inaccessible $class magically.
    public function __get($class)
    {
        if(isset($this->objects[$class]))
        {
            return $this->objects[$class];
        }
        return $this->objects[$class] = new $class($this->deps);
    }

    public function methodFoo()
    {
         return $this->Boo->methodBoo();
    }
}

$Foo = new Foo();
$Foo->deps = 'says hello';
var_dump($Foo->methodFoo());

result,

string 'Boo method says hello' (length=21)

I don't want to use construction injection in some cases because not all methods in Foo rely on the same injections. For instance,methodFoo()in Foo relies on Boo only, while other methods rely on other classes/ injections.

Also, I don't want to use setter injection either because I might have to write lots of them in Foo, like

setBoo() {}
setToo() {}
setLoo() {}
... and so on...

So I thought using the magic method __get and __set could save me from ending up writing a long list of them. With this, I only 'inject' the dependency when it is needed by a method in Foo.

Is this correct way of doing it? I haven't done any test with an unit test before. Can this solution be tested?

Or any better solutions you have got?


Solution

  • Don't Use Magic Methods If Possible...

    Don't use magic methods if possible as it can make it very difficult for yourself or anyone else to come back at a later date and understand where and how certain objects were injected (even when using a good IDE). These __set and __get magic methods are not a long term solution to your problem and will only add confusion in the long run.

    As you already know you can use 'constructor' injection for setting properties and injecting objects that are 'required' during instantiation of your object.

    Alternatively, if you have dependencies that are 'optional' then use setter / getter methods. That way you 'know' what objects your class uses to perform it's function.

    If your class needs say 5 or more dependencies (required or optional) than perhaps your class is try to do to much. Break it down into smaller classes that require less dependencies and you will find your code not only becomes more readable / understandable but also more modular and reusable. (Separation of concerns, etc.)

    Regarding testing of a class that uses magic methods, I'm sure it can be done but at much more pains than if one didn't use magic methods.

    Google 'Design Patterns'. What you find and learn about design patterns will improve the way you 'join' or 'wire' your classes together.