Di and EventManager

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Di and EventManager

Axel
Hi,

I'm going to get familiar with Zend\Di and faced some disadvantages.

1) Zend\Di\DependencyInjectionInterface emms not to define all methods
i.e. get()

2) There is no InstanceManagerInterface. Its commendet in
InstanceManager and typehints in Di point to InstanceManager
implementation instead of an interface. This requires custom
implementations to extend InstanceManager.

3) Every dependency defined is stuffed into the object on creation no
matter if it actually is used for the current program flow. This is a
performance penalty in PHP. Actually it is the same problem as with
require_once calls. Since PHP is share nothing, every dependency (and
all dependencies of the dependencies recursively) is initialized on
every page request which is quite expensive.
This can be bypassed with AOP in other languages like Java but
unfortunately PHP does not offer AOP (and probably won't provide it in
near future).
There should be some kind of lazy injection to avoid this issue. Maybe
the event system could help.

I thought about something like this:

use Zend\Di\Event as DiEvent
...

/**
  * Inject Foo
  *
  * @inject lazy
  * @param Foo $foo
  */
public funtion setFoo(Foo $foo)
{
     ...
}

/**
  * Ferturn the foo instance
  */
protected function getFoo() {
     if (!$this->foo) {
         $event = new DiEvent($this);
         $this->events()->triggerEvent(__METHOD__, $event);

         // or for convenience which eliminates the additional class import

         $this->di()->triggerEvent(__METHOD__, $this);
     }

     return $this->foo;
}


Personally I'd prefer every non-scalar injection to  be treaded lazy
until typehinted otherwise.
Any suggestions?

Regards,
Axel
Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

Marco Pivetta
Hi Axel,

Except for the injections stuff, on which I totally disagree since object instantiation is not a problem at all when it comes to performance (and the user itself should use a ServiceLocator to have lazy loading in place in such cases), could you eventually point us to some code (just link the lines on github)? I have an open PR on the Di component, so I could just include your fixes if it is not that invasive :)


Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 20 June 2012 08:43, Axel <[hidden email]> wrote:
Hi,

I'm going to get familiar with Zend\Di and faced some disadvantages.

1) Zend\Di\DependencyInjectionInterface emms not to define all methods i.e. get()

2) There is no InstanceManagerInterface. Its commendet in InstanceManager and typehints in Di point to InstanceManager implementation instead of an interface. This requires custom implementations to extend InstanceManager.

3) Every dependency defined is stuffed into the object on creation no matter if it actually is used for the current program flow. This is a performance penalty in PHP. Actually it is the same problem as with require_once calls. Since PHP is share nothing, every dependency (and all dependencies of the dependencies recursively) is initialized on every page request which is quite expensive.
This can be bypassed with AOP in other languages like Java but unfortunately PHP does not offer AOP (and probably won't provide it in near future).
There should be some kind of lazy injection to avoid this issue. Maybe the event system could help.

I thought about something like this:

use Zend\Di\Event as DiEvent
...

/**
 * Inject Foo
 *
 * @inject lazy
 * @param Foo $foo
 */
public funtion setFoo(Foo $foo)
{
   ...
}

/**
 * Ferturn the foo instance
 */
protected function getFoo() {
   if (!$this->foo) {
       $event = new DiEvent($this);
       $this->events()->triggerEvent(__METHOD__, $event);

       // or for convenience which eliminates the additional class import

       $this->di()->triggerEvent(__METHOD__, $this);
   }

   return $this->foo;
}


Personally I'd prefer every non-scalar injection to  be treaded lazy until typehinted otherwise.
Any suggestions?

Regards,
Axel

Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

Axel
Hi Marco,

I'll add changes for the Interfaces stuff to my github fork. Do you want me to trigger a pull request?

To the Injections-Stuff:
Well I partially agree with you, but it depends on the DI config. Defining every dependency and having a lot of them will definitely slow down. I'd suggest for the reflection definition generator, to only require setter dependencies marked via annotations. I'd like to have "soft" dependencies which are injectable via getter (i.e. DI config in unit tests) but they're allowed to be omitted and will then be auto discovered via ServiceLocator. Maybe this is already the case?

I'm fine with the ServiceLocator approach as well.
I guess should look like this:

public function getFoo()
{
    if (!$this->foo) {
        $this->setFoo($this->getServiceLocator()->get('my.company.component.foo'));
    }

    return $this->foo;
}


On 20.06.2012 10:01, Marco Pivetta wrote:
Hi Axel,

Except for the injections stuff, on which I totally disagree since object instantiation is not a problem at all when it comes to performance (and the user itself should use a ServiceLocator to have lazy loading in place in such cases), could you eventually point us to some code (just link the lines on github)? I have an open PR on the Di component, so I could just include your fixes if it is not that invasive :)


Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 20 June 2012 08:43, Axel <[hidden email]> wrote:
Hi,

I'm going to get familiar with Zend\Di and faced some disadvantages.

1) Zend\Di\DependencyInjectionInterface emms not to define all methods i.e. get()

2) There is no InstanceManagerInterface. Its commendet in InstanceManager and typehints in Di point to InstanceManager implementation instead of an interface. This requires custom implementations to extend InstanceManager.

3) Every dependency defined is stuffed into the object on creation no matter if it actually is used for the current program flow. This is a performance penalty in PHP. Actually it is the same problem as with require_once calls. Since PHP is share nothing, every dependency (and all dependencies of the dependencies recursively) is initialized on every page request which is quite expensive.
This can be bypassed with AOP in other languages like Java but unfortunately PHP does not offer AOP (and probably won't provide it in near future).
There should be some kind of lazy injection to avoid this issue. Maybe the event system could help.

I thought about something like this:

use Zend\Di\Event as DiEvent
...

/**
 * Inject Foo
 *
 * @inject lazy
 * @param Foo $foo
 */
public funtion setFoo(Foo $foo)
{
   ...
}

/**
 * Ferturn the foo instance
 */
protected function getFoo() {
   if (!$this->foo) {
       $event = new DiEvent($this);
       $this->events()->triggerEvent(__METHOD__, $event);

       // or for convenience which eliminates the additional class import

       $this->di()->triggerEvent(__METHOD__, $this);
   }

   return $this->foo;
}


Personally I'd prefer every non-scalar injection to  be treaded lazy until typehinted otherwise.
Any suggestions?

Regards,
Axel


Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

Marco Pivetta
The ServiceLocator approach is the approach to go, while changing the behavior of Zend\Di in a way that makes it more performing basically breaks the component's main functionality in my opinion.

I am working on a module that aims at solving this performance problem via Di compilation (see http://packagist.org/packages/ocramius/ocra-di-compiler / https://github.com/Ocramius/OcraDiCompiler). It is still a work in progress, but basically works once my currently open PR is merged, and I'm also already testing it. Otherwise, the performance problem has to be solved via ServiceManager factories.

Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 20 June 2012 12:53, Axel <[hidden email]> wrote:
Hi Marco,

I'll add changes for the Interfaces stuff to my github fork. Do you want me to trigger a pull request?

To the Injections-Stuff:
Well I partially agree with you, but it depends on the DI config. Defining every dependency and having a lot of them will definitely slow down. I'd suggest for the reflection definition generator, to only require setter dependencies marked via annotations. I'd like to have "soft" dependencies which are injectable via getter (i.e. DI config in unit tests) but they're allowed to be omitted and will then be auto discovered via ServiceLocator. Maybe this is already the case?

I'm fine with the ServiceLocator approach as well.
I guess should look like this:

public function getFoo()
{
    if (!$this->foo) {
        $this->setFoo($this->getServiceLocator()->get('my.company.component.foo'));
    }

    return $this->foo;

}


On 20.06.2012 10:01, Marco Pivetta wrote:
Hi Axel,

Except for the injections stuff, on which I totally disagree since object instantiation is not a problem at all when it comes to performance (and the user itself should use a ServiceLocator to have lazy loading in place in such cases), could you eventually point us to some code (just link the lines on github)? I have an open PR on the Di component, so I could just include your fixes if it is not that invasive :)


Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 20 June 2012 08:43, Axel <[hidden email]> wrote:
Hi,

I'm going to get familiar with Zend\Di and faced some disadvantages.

1) Zend\Di\DependencyInjectionInterface emms not to define all methods i.e. get()

2) There is no InstanceManagerInterface. Its commendet in InstanceManager and typehints in Di point to InstanceManager implementation instead of an interface. This requires custom implementations to extend InstanceManager.

3) Every dependency defined is stuffed into the object on creation no matter if it actually is used for the current program flow. This is a performance penalty in PHP. Actually it is the same problem as with require_once calls. Since PHP is share nothing, every dependency (and all dependencies of the dependencies recursively) is initialized on every page request which is quite expensive.
This can be bypassed with AOP in other languages like Java but unfortunately PHP does not offer AOP (and probably won't provide it in near future).
There should be some kind of lazy injection to avoid this issue. Maybe the event system could help.

I thought about something like this:

use Zend\Di\Event as DiEvent
...

/**
 * Inject Foo
 *
 * @inject lazy
 * @param Foo $foo
 */
public funtion setFoo(Foo $foo)
{
   ...
}

/**
 * Ferturn the foo instance
 */
protected function getFoo() {
   if (!$this->foo) {
       $event = new DiEvent($this);
       $this->events()->triggerEvent(__METHOD__, $event);

       // or for convenience which eliminates the additional class import

       $this->di()->triggerEvent(__METHOD__, $this);
   }

   return $this->foo;
}


Personally I'd prefer every non-scalar injection to  be treaded lazy until typehinted otherwise.
Any suggestions?

Regards,
Axel



Reply | Threaded
Open this post in threaded view
|

RE: Di and EventManager

Roediger, Tim
In reply to this post by Axel

Lazy loading of dependencies would make circular dependencies possible also…

 

Tim Roediger

 

From: Axel [mailto:[hidden email]]
Sent: Wednesday, 20 June 2012 8:53 PM
To: Marco Pivetta
Cc: [hidden email]
Subject: Re: [zf-contributors] Di and EventManager

 

Hi Marco,

I'll add changes for the Interfaces stuff to my github fork. Do you want me to trigger a pull request?

To the Injections-Stuff:
Well I partially agree with you, but it depends on the DI config. Defining every dependency and having a lot of them will definitely slow down. I'd suggest for the reflection definition generator, to only require setter dependencies marked via annotations. I'd like to have "soft" dependencies which are injectable via getter (i.e. DI config in unit tests) but they're allowed to be omitted and will then be auto discovered via ServiceLocator. Maybe this is already the case?

I'm fine with the ServiceLocator approach as well.
I guess should look like this:

public function getFoo()
{
    if (!$this->foo) {
        $this->setFoo($this->getServiceLocator()->get('my.company.component.foo'));
    }

    return $this->foo;
}


On 20.06.2012 10:01, Marco Pivetta wrote:

Hi Axel,

Except for the injections stuff, on which I totally disagree since object instantiation is not a problem at all when it comes to performance (and the user itself should use a ServiceLocator to have lazy loading in place in such cases), could you eventually point us to some code (just link the lines on github)? I have an open PR on the Di component, so I could just include your fixes if it is not that invasive :)


Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    


On 20 June 2012 08:43, Axel <[hidden email]> wrote:

Hi,

I'm going to get familiar with Zend\Di and faced some disadvantages.

1) Zend\Di\DependencyInjectionInterface emms not to define all methods i.e. get()

2) There is no InstanceManagerInterface. Its commendet in InstanceManager and typehints in Di point to InstanceManager implementation instead of an interface. This requires custom implementations to extend InstanceManager.

3) Every dependency defined is stuffed into the object on creation no matter if it actually is used for the current program flow. This is a performance penalty in PHP. Actually it is the same problem as with require_once calls. Since PHP is share nothing, every dependency (and all dependencies of the dependencies recursively) is initialized on every page request which is quite expensive.
This can be bypassed with AOP in other languages like Java but unfortunately PHP does not offer AOP (and probably won't provide it in near future).
There should be some kind of lazy injection to avoid this issue. Maybe the event system could help.

I thought about something like this:

use Zend\Di\Event as DiEvent
...

/**
 * Inject Foo
 *
 * @inject lazy
 * @param Foo $foo
 */
public funtion setFoo(Foo $foo)
{
   ...
}

/**
 * Ferturn the foo instance
 */
protected function getFoo() {
   if (!$this->foo) {
       $event = new DiEvent($this);
       $this->events()->triggerEvent(__METHOD__, $event);

       // or for convenience which eliminates the additional class import

       $this->di()->triggerEvent(__METHOD__, $this);
   }

   return $this->foo;
}


Personally I'd prefer every non-scalar injection to  be treaded lazy until typehinted otherwise.
Any suggestions?

Regards,
Axel

 

 

Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

Axel
In reply to this post by Marco Pivetta
Hi Marco,

Am 20.06.2012 10:01, schrieb Marco Pivetta:
Hi Axel,

Except for the injections stuff, on which I totally disagree since object instantiation is not a problem at all when it comes to performance (and the user itself should use a ServiceLocator to have lazy loading in place in such cases), could you eventually point us to some code (just link the lines on github)? I have an open PR on the Di component, so I could just include your fixes if it is not that invasive :)

As requested here is the link to the commit in github:
https://github.com/tux-rampage/zf2/commit/435befdcd4b52b18262c77d19f9fd8ab4dc8e65d


Best,
Axel
Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

Axel
In reply to this post by Roediger, Tim
Circular Dependencies are always possible.
Doesn't Zend\Di implement a call stack to prevent this?

Am 20.06.2012 23:49, schrieb Roediger, Tim:

Lazy loading of dependencies would make circular dependencies possible also…



Reply | Threaded
Open this post in threaded view
|

Re: Di and EventManager

ralphschindler
No, it traverses an objects definition filling dependencies as needed,
while building up the object.

Once an object has been instantiated, it is put inside the
InstanceManager container before any setter based dependencies are
fulfilled.  And it basically, at that point breaks down like this:

   * Circular dependencies from constructor to constructor are
impossible to resolve, and you'll get an exception

   * Circular dependencies from setter to constructor/setter
dependencies can be resolved since already constructed instances
(without setter dependencies) will already be in the container

 From a pattern point of view, its not really Lazy Loaded, per-se, but
it is on-demand instantiation.

-ralph

On 6/21/12 2:49 AM, Axel wrote:

> Circular Dependencies are always possible.
> Doesn't Zend\Di implement a call stack to prevent this?
>
> Am 20.06.2012 23:49, schrieb Roediger, Tim:
>>
>> Lazy loading of dependencies would make circular dependencies possible
>> also…
>>
>>
>

Reply | Threaded
Open this post in threaded view
|

RE: Di and EventManager

Roediger, Tim
In reply to this post by Axel

Hi Axel,

 

Yes Zend\Di has a call stack to prevent circular dependencies.

 

What I was meaning is that with lazy loading circular dependencies wouldn’t break things – ie, they could be used, where as currently they cannot. Like when you might want to inject a class with itself.

 

Tim Roediger

 

From: Axel [mailto:[hidden email]]
Sent: Thursday, 21 June 2012 5:49 PM
To: Roediger, Tim
Cc: Marco Pivetta; [hidden email]
Subject: Re: [zf-contributors] Di and EventManager

 

Circular Dependencies are always possible.
Doesn't Zend\Di implement a call stack to prevent this?

Am 20.06.2012 23:49, schrieb Roediger, Tim:

Lazy loading of dependencies would make circular dependencies possible also…

 

 

Reply | Threaded
Open this post in threaded view
|

RE: Di and EventManager

Roediger, Tim
In reply to this post by ralphschindler
My mistake, Ralph knows all.

Tim Roediger


-----Original Message-----
From: Ralph Schindler [mailto:[hidden email]]
Sent: Friday, 22 June 2012 3:26 AM
To: [hidden email]
Subject: Re: [zf-contributors] Di and EventManager

No, it traverses an objects definition filling dependencies as needed, while building up the object.

Once an object has been instantiated, it is put inside the InstanceManager container before any setter based dependencies are fulfilled.  And it basically, at that point breaks down like this:

   * Circular dependencies from constructor to constructor are impossible to resolve, and you'll get an exception

   * Circular dependencies from setter to constructor/setter dependencies can be resolved since already constructed instances (without setter dependencies) will already be in the container

 From a pattern point of view, its not really Lazy Loaded, per-se, but it is on-demand instantiation.

-ralph

On 6/21/12 2:49 AM, Axel wrote:

> Circular Dependencies are always possible.
> Doesn't Zend\Di implement a call stack to prevent this?
>
> Am 20.06.2012 23:49, schrieb Roediger, Tim:
>>
>> Lazy loading of dependencies would make circular dependencies
>> possible also…
>>
>>
>