2

I'm developing a PHP framework for educational purposes. I have learned a lot since I started it.

I've decided how I'm going to deal with dependencies. I'm create a simple DI Container.


My first question is not about the DI Container itself, but how to inject objects that are created outside (before the DI Container).

Q: In the example: I am calling container->manualAdd('_logger', $logger);. Is there another way to accomplish this? Am I breaking the idea of DI Container?


My second question is about hooking functions. So when in bootstrap all objects are instantiated, objects by it selves can now begin to function.

Q: In the example: I'm creating an EventDispatcher. Whoever needs to do something either on doneBuild or beforeTerminate, is injected with BootstrapEventDispatcher. Is there another way to do this?

I begin to think EventDispatcher is overkill (for bootstrap only), and maybe implement something like: CodeIgniter:Hooks

Any help is appreciated.

Example bootstrap (pseudo-code):

function bootstrap($file = 'file.xml'){
    $logger = new Logger();
    $logger->log('bootstrap: init');

    $dispatcher = new BootstrapEventDispatcher($logger);

    $container = new DIContainer(new ConfigReader($file), $logger);
    $container->manualAdd('_logger', $logger);
    $container->manualAdd('_bootstrap_event_dispatcher', $dispatcher);
    $container->build();
    $dispatcher->doneBuild(null, new EventArgs());

    $dispatcher->beforeTerminate(null, new EventArgs());
    $logger->log('bootstrap: terminate');
}
class DIContainer{
    public function build(){
        //read xmls and create classes, etc.
        $this->logger->log('DIContainer: creating objects: {objects}');
    }
}

Example of an xml:

<!-- example file.xml !-->
<services>
    <service id="simple_class" class="SimpleClass"></service>
    <service id="complex_class" class="ComplexClass">
        <argument type="service" id="simple_class" /> <!-- dependency injection !-->
        <argument type="service" id="_logger" /> <!-- dependency injection !-->
        <argument type="service" id="_bootstrap_event_dispatcher" /> <!-- dependency injection !-->
    </service>
</services>

Example of ComplexClass:

class ComplexClass{
    public function __construct(SimpleClass $simpleClass, BootstrapEventDispatcher $dispatcher, Logger $logger){
        $this->simpleClass = $simpleClass;
        $this->logger = $logger;
        $dispatcher->onDoneBuild(array($this, 'onBootstrapDoneBuild'));
    }
    public function onBootstrapDoneBuild($obj, $args){
        //do something.
        $this->logger->log('complexclass: did something');
    }
}
2
  • 1
    Have you looked at any of the existing DIC implementations such as Pimple? (pimple.sensiolabs.org) Might give you some ideas. Both logger and your dispatcher should be created by the container itself when needed. Silex (silex.sensiolabs.org/doc/services.html) is a micro framework whose main application class extends Pimple. It might give you some ideas on how to bootstrap things. Commented Jun 5, 2013 at 19:39
  • I'm looking at Silex and understanding how they do it. Thanks! Commented Jun 5, 2013 at 20:16

1 Answer 1

2

From my understanding in Silex/Symfony2, is that there is no 'magical way' to do these type of things.

For my first question: It is allowed to add objects that are created before the container.

In Symfony2, in Kernel:initializeContainer function, Kernel adds itself to the container ($this->container->set('kernel', $this);) and later on, in the xml files, services are injected with Kernel (<argument id="kernel" type="service" />).

In Silex, Application:__construct function creates and adds objects to the container. Application injects itself to the ServiceProviders, so these providers can inject dependencies to their objects and add them to the container.

$container->manualAdd('_logger', $logger); is correct.


For my second question: depends on how I want to handle it. I came up with 3 options:

.1 For C#-event-like, Kernel is added into the container:

In ComplexClass: kernel.terminate += kernelTerminate

.2 EventDispatcher (as long as EventDispatcher class doesn't need dependencies that are created in the xml files)

//bootstrap function:
$dispatcher = new KernelEventDispatcher();
$kernel = new Kernel($dispatcher);
$container->manualAdd('_kernel.dispatcher');

.

<!-- in file.xml:ComplexClass !-->
<argument id="_kernel.dispatcher" type="service" />

.3 Create an object that implements an interface: (I couldn't find another name for Runnable)

<!-- in file.xml !-->
<service id="complex_class_runnable" class="ComplexClassRunnable">
    <argument type="service" id="complex_class" />
    <argument type="service" id="_kernel" />
</service>

.

//in ComplexClassRunnable
$kernel->addRunnable($this);

.

//in Kernel
foreach($this->runnables as $runnable){
    $runnable->init(); //same for terminate
}

Bootstrap updated:

function bootstrap($file = 'file.xml'){
    $logger = new Logger();
    $logger->log('bootstrap: init');

    $kernel = new Kernel($logger);
    $container = new DIContainer(new ConfigReader($file), $logger);

    $container->manualAdd('_kernel', $kernel);
    $container->manualAdd('_logger', $logger);
    $container->build();

    $kernel->boot();

    $logger->log('bootstrap: terminate');
}
class DIContainer{
    public function build(){
        //read xmls and create classes, etc.
        $this->logger->log('DIContainer: creating objects: {objects}');
    }
}
class Kernel{
    public function boot(){
        //...
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.