DI: pass through long chain of classes or use statically?

I didn't find out how to format spoilers, so it'll be kinda long(

Let's say, I need to use object method that requires some service within dependency injection container. For example, logger service. And let's say that this object is created inside method of another class that may or may not require DIC. Let's also assume that it may be nested deeper than 2 levels.

How should I inform that bottom-level class about DIC presense?

Should I pass it through all classes, possibly adding DIC knowledge to objects that don't need it?

Something like:

index.php:

use Phalcon\Di\FactoryDefault\Cli;
use Phalcon\Logger\Adapter\File;

$di = new Cli();

$di->setShared('logger', function () {
    return new File('app.log');
});

$di->set('creator', 'CreatorClass');
$oCreator = $di->get('creator');

$oCreator->doSomeStuff();

CreatorClass.php:

use Phalcon\Di\InjectionAwareInterface;

class CreatorClass implements InjectionAwareInterface
{
    protected $_di;

    public function setDI(\Phalcon\DiInterface $dependencyInjector)
    {
        $this->_di = $dependencyInjector;
    }

    public function getDI()
    {
        return $this->_di;
    }

    public function doSomeStuff()
    {
        $this->_di->set('utilizer', 'UtilizerClass');

        $oUtilizer = $this->_di->get('utilizer');
        $oUtilizer->useProvider();
    }
}

UtilizerClass.php:

use Phalcon\Di\InjectionAwareInterface;

class UtilizerClass implements InjectionAwareInterface
{
    protected $_di;

    public function setDI(\Phalcon\DiInterface $dependencyInjector)
    {
        $this->_di = $dependencyInjector;
    }

    public function getDI()
    {
        return $this->_di;
    }

    public function useProvider()
    {
        $this->_di->set('provider', 'ProviderClass');

        $oProvider = $this->_di->get('provider');
        echo $oProvider->getData();
        echo $oProvider->getData();
    }

}

ProviderClass.php:

use Phalcon\Di\InjectionAwareInterface;

class ProviderClass implements InjectionAwareInterface
{
    protected $_di;

    public function setDI(\Phalcon\DiInterface $dependencyInjector)
    {
        $this->_di = $dependencyInjector;
    }

    public function getDI()
    {
        return $this->_di;
    }

    public function getData()
    {
        $data = 'data';

        $oLogger = $this->_di->get('logger');
        $oLogger->info($data . ' retrieved');

        return $data;
    }
}

Or, should i use DI statically only where it really need to be used?

Something like: index.php:

use Phalcon\Di\FactoryDefault\Cli;
use Phalcon\Logger\Adapter\File;

$di = new Cli();

$di->setShared('logger', function () {
    return new File('app.log');
});

$oCreator = new CreatorClass();
$oCreator->doSomeStuff();

CreatorClass.php:

class CreatorClass
{

    public function doSomeStuff()
    {
        $oUtilizer = new UtilizerClass();
        $oUtilizer->useProvider();
    }
}

UtilizerClass.php:

class UtilizerClass
{
    public function useProvider()
    {
        $oProvider = new ProviderClass();
        echo $oProvider->getData();
    }

}

ProviderClass.php:

use Phalcon\Di;

class ProviderClass
{

    public function getData()
    {
        $data = 'data';

        $oLogger = Di::getDefault()->get('logger');
        $oLogger->info($data . ' retrieved');

        return $data;
    }
}

Or, maybe, I should use some other approach?

edited Jan '16

hi. this can be done in a few ways.

  1. Extend your class with Phalcon/Di/injectable
  2. implement injectionawareinterface on your class and code getDi/setDi and pass your di as a varible
  3. custom method by passing your di as a variable.

latley i extend my classes with phalcon/di/injectable since it is easy and also provide you with the eventmanager

Like:

abstract class RexecProvider extends Injectable implements RexecInterface
{

    private $ip;
    private $port;
    private $rcexec_password;
    private $error;
    private $status;
    //protected $logger;
    protected $di;
    protected $statusserver=null;

    /**
     * Constructor for the new class
     */
    public function __construct() {
        $this->di=$this->getDI();
        //$this->logger=$this->di['logger'];
        $this->di['logger']->info("RExec loaded");
    }

Regards André



1.2k
edited Jan '16

So, your actual answer (in case of Injectable solution) is to:

  1. pass DI through all objects regardless of whether or not they really need to know about DIC in the first place;
  2. use inheritance to make my code more coupled and decrease possibility to extend really important classes.
edited Jan '16

So, your actual answer (in case of Injectable solution) is to:

  1. pass DI through all objects regardless of whether or not they really need to know about DIC in the first place;
  2. use inheritance to make my code more coupled and decrease possibility to extend really important classes.

well yes where it is needed, A long chain of classes really needs planing. If you look at some major phalcon projects you see that

public function setDi($di) {
        $this->_di=$di;
    }

is commonly present somehere in the class chain (suggestion 2 from my initial suggestion) and you dont need to implement the interface but it is good practise from an api point of view

You can also use Phalcon\DI::getDefault() to get the lastest created DI see: https://docs.phalconphp.com/en/latest/api/Phalcon_DI.html

$logger = \Phalcon\DI::getDefault()->get('logger');

Just as you described, the choice is yours. But Im schooled to avoid statics as much as possible. And it makes perfect sense, especially on DI containers, to pass the variables in the constructor.

Regards