Quantcast

Services/Instances/Dependencies in ZF2

classic Classic list List threaded Threaded
34 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Services/Instances/Dependencies in ZF2

ralphschindler
Hey Contributors,

There is no TL;DR for this email as this email is a _must-read_ for
those developing ZF2 applications and modules.

== Background ==

Currently, Zend Framework 2 MVC based application infrastructure
requires that Zend\Di play an integral part in the application
setup/bootstrap and dispatching.  Zend\Di is one part complex one part
magical for many individuals.  And, while it has many benefits in the
areas of flexibility and service management, it does present some issues
in the performance and complexity areas.

Even if we were to solve the performance problem with Zend\Di by
"compiling definitions" (I actually don't think we'll see that much
improvement by doing this), we still have the complexity issue.  At
first sight, developers must, instead of writing code, write
configuration (metadata) about code - and this, to me and some others,
presents and immediate turn-off as a developer now has to familiarize
themselves intimately with Zend\Di in order to start building real
applications.  This is called Meta-Programming.

I'd like to solve this problem with a new component that will be the
minimal dependency in the MVC stack, but we can also retain the benefits
of Zend\Di.  The pattern at play is Service Location.  And while I've
named this component a number of things in past incarnations:
Zend\ServiceLocator and Zend\InstanceManager.. The final implementation
of this component is called Zend\ServiceManager.

The name is chosen so that it mirrors other core components in the ZF2
life-cycle: Zend\EventManager and Zend\Module (I'd actually support
renaming this to Zend\ModuleManager, but that is a separate issue.)

How does this solve our problems we have with Zend\Di?

   * First, it solves the problems of service sharing and service
     location though a simpler implementation

   * The Zend\ServiceManager implementation does not concern itself
     with auto-initialization and auto-wiring of instances and
     dependencies.

   * It provides developers a richer infrastructure to allow them
     to create service factories that do the service initialization
     and wiring of dependencies.  This is as opposed to Zend\Di's
     mantra of writing configuration that will influence an object
     which will create and wire instances and dependencies for you.

   * Which means there is no expensive configuration to class
     structure mapping that needs to happen in order to create
     a service/instance/object.

This does not mean that Zend\Di goes away, on the contrary.  Zend\Di is
still wired as a default service of the MVC stack, and is also presented
through Zend\ServiceManager as a fallback option when a defined service
cannot be located.

In short, this allows consumers of ZF2 a more lightweight and faster
infrastructure to build their applications on.  This also allows for
usage of Zend\Di for RAD development and more complex service situations.

== How it Works ==

In Zend\ServiceManager, instances can be created primarily through
objects/classes that either implement the FactoryInterface, or are
closueres, or are callable.  These factories are attached to particular
service name, like "DbAdapter", for example.  When someone calls
$serviceManager->get('DbAdapter');, it will check to see if that
instance was already created; if it wasn't, it will search for a factory
to create that instance.  All factories/closures/callable have access to
the ServiceManager to be able to resolve any dependencies, should they
need to do this.

This is the basis of Service Location.

Other similar, but more lightweight implementations of this same
principle can be explored in Fabien's Pimple
(http://pimple.sensiolabs.org/ and also Tweetie http://twittee.org/.  In
my mind, neither of these qualify as a DI Container as neither do
"auto-instantiation" or "auto-wiring" of instances and dependencies ...
these are really service location containers.

== Seeing it in Action ==

First, you should review the primary API of the ServiceManager here:

   * In my feature/service-manager branch:
 
https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/ServiceManager

Then, you should review how this is integrated into Zend\Mvc (same branch)

   *
https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/Mvc

Next, have a look at the skeleton application that consumes both
Zend\Mvc and Zend\ServiceManager:

   * Again, inside a feature/service-manager branch:
 
https://github.com/ralphschindler/ZendSkeletonApplication/tree/feature/service-manager

Things to look out for:

   * There is a factory per service, you can examine that inside
     the Zend\Mvc\Service namespace.  The view aspect of this can
     more than likely be reduced to simply a View service that
     has its dependencies composed in as each dependency really
     does not need to be a service of its own.

   * The general perspective (as demonstrated by the index.php
     file
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/public/index.php)
     is that an application is a collection of services that when
     wired together, are an "application".  By asking the
     Application object to run() and send(), it will coordinate
     with all the other services in the service manager to do
     what is necessary to run() and send().

   * How do I create a service?  You write a factory object,
     then register through the Module in which it resides:

     * For example a DbAdapter:
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/DbAdapterFactory.php

     * And its registration:
 
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L54

   * How do override a base service?  Such as the ViewResolver?

     * This can be achieved through the application.config.php file:
 
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/config/application.config.php#L25

     * And the factory to produce this alternate ViewResolver:
 
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/ViewResolverFactory.php

   * How Do I use Zend\Di during development and for my Application code?

     * Use it as normal.  If a service is not registered with the
       Service Manager it will use Di to create that service, for
       example, inside a controller:

 
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Controller/IndexController.php#L13

       Since MovieFinder is not registered service, Zend\Di will
       produce this instance (and through reflection, determine
       that it needs to use a Db Adapter.)

   * But how does Zend\Di share instances with Zend\ServiceManager?

     * Easy, by telling Zend\ServiceManager that an alias exists
       for DbAdapter as "Zend\Db\Adapter\Adapter", when Di goes to
       look for this class, it will consult the ServiceManager for
       a service by that name (which happens to be the class name):

 
https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L57


== Long Term Benefits ==

Since Zend\ServiceManager Factories are a very concrete thing, Zend\Di
can be fashioned in a way to create service factories for you.  It will
do this since it already knows how things are created and wired together
already.  It will take the information and create service factory
classes.  This step will be a compilation step, and to draw a parallel,
is similar in nature to how Doctrine needs to create Proxy classes.  In
as sense, it is code generation.  This means that after compilation,
Zend\Di will not need to be consulted as to how to build and wire an
instance of something.

This could be exposed as either:

   * A button inside of a Developer toolbar
   * A command line script "compile-services" exposed via zf2.php perhaps

Will you need to do this?  No, since the majority of the infrastucture
and shipped modules would (should) have their own Factories (instead of
leveraging DI), things will already be as fast as they can be.  Zend\Di
will be in place for the application developer to decide when and how to
generate service factories.



Discuss!
-ralph


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

EvanDotPro
Ralph,

Excellent work. I really like how you've tied in with the existing module infrastructure nicely and kept the idea of a merged configuration. I like the idea of Configuration as a service which can then be accessed and used by the other services for things like the view path stack. Retaining the concept of configuration merging and using those merged parameters in the services retains certain parts of the flexibility we were getting from DI, which is important to me.

I'm still combing over this, so more feedback may follow, but so far, so good. I plan on going through a few use-case scenarios such as things I'm doing with swapping mappers in ZfcUser to make sure they're all still possible, but so far, it's all playing out well in my head with what you've presented here.

Good work and thanks for this much-needed addition!

---
Evan Coury, ZCE

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

xoops
In reply to this post by ralphschindler
The Zend\ServiceManager is good as long as it is not dependent on Zend\Di.
Actually we already build such a service manager in our new application framework which utilizes ZF2 as the library.

BTW, it would be appreciated if we first make the ZF2 framework itself solid before thinking of those so-called modules.
Make the ZF2 solid and ready as soon as possible, leave application modules to users who need modules.

Cheers!

On Wed, Apr 25, 2012 at 3:48 AM, Ralph Schindler <[hidden email]> wrote:
Hey Contributors,

There is no TL;DR for this email as this email is a _must-read_ for those developing ZF2 applications and modules.

== Background ==

Currently, Zend Framework 2 MVC based application infrastructure requires that Zend\Di play an integral part in the application setup/bootstrap and dispatching.  Zend\Di is one part complex one part magical for many individuals.  And, while it has many benefits in the areas of flexibility and service management, it does present some issues in the performance and complexity areas.

Even if we were to solve the performance problem with Zend\Di by "compiling definitions" (I actually don't think we'll see that much improvement by doing this), we still have the complexity issue.  At first sight, developers must, instead of writing code, write configuration (metadata) about code - and this, to me and some others, presents and immediate turn-off as a developer now has to familiarize themselves intimately with Zend\Di in order to start building real applications.  This is called Meta-Programming.

I'd like to solve this problem with a new component that will be the minimal dependency in the MVC stack, but we can also retain the benefits of Zend\Di.  The pattern at play is Service Location.  And while I've named this component a number of things in past incarnations: Zend\ServiceLocator and Zend\InstanceManager.. The final implementation of this component is called Zend\ServiceManager.

The name is chosen so that it mirrors other core components in the ZF2 life-cycle: Zend\EventManager and Zend\Module (I'd actually support renaming this to Zend\ModuleManager, but that is a separate issue.)

How does this solve our problems we have with Zend\Di?

 * First, it solves the problems of service sharing and service
   location though a simpler implementation

 * The Zend\ServiceManager implementation does not concern itself
   with auto-initialization and auto-wiring of instances and
   dependencies.

 * It provides developers a richer infrastructure to allow them
   to create service factories that do the service initialization
   and wiring of dependencies.  This is as opposed to Zend\Di's
   mantra of writing configuration that will influence an object
   which will create and wire instances and dependencies for you.

 * Which means there is no expensive configuration to class
   structure mapping that needs to happen in order to create
   a service/instance/object.

This does not mean that Zend\Di goes away, on the contrary.  Zend\Di is still wired as a default service of the MVC stack, and is also presented through Zend\ServiceManager as a fallback option when a defined service cannot be located.

In short, this allows consumers of ZF2 a more lightweight and faster infrastructure to build their applications on.  This also allows for usage of Zend\Di for RAD development and more complex service situations.

== How it Works ==

In Zend\ServiceManager, instances can be created primarily through objects/classes that either implement the FactoryInterface, or are closueres, or are callable.  These factories are attached to particular service name, like "DbAdapter", for example.  When someone calls $serviceManager->get('DbAdapter');, it will check to see if that instance was already created; if it wasn't, it will search for a factory to create that instance.  All factories/closures/callable have access to the ServiceManager to be able to resolve any dependencies, should they need to do this.

This is the basis of Service Location.

Other similar, but more lightweight implementations of this same principle can be explored in Fabien's Pimple (http://pimple.sensiolabs.org/ and also Tweetie http://twittee.org/.  In my mind, neither of these qualify as a DI Container as neither do "auto-instantiation" or "auto-wiring" of instances and dependencies ... these are really service location containers.

== Seeing it in Action ==

First, you should review the primary API of the ServiceManager here:

 * In my feature/service-manager branch:

https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/ServiceManager

Then, you should review how this is integrated into Zend\Mvc (same branch)

 * https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/Mvc

Next, have a look at the skeleton application that consumes both Zend\Mvc and Zend\ServiceManager:

 * Again, inside a feature/service-manager branch:

https://github.com/ralphschindler/ZendSkeletonApplication/tree/feature/service-manager

Things to look out for:

 * There is a factory per service, you can examine that inside
   the Zend\Mvc\Service namespace.  The view aspect of this can
   more than likely be reduced to simply a View service that
   has its dependencies composed in as each dependency really
   does not need to be a service of its own.

 * The general perspective (as demonstrated by the index.php
   file https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/public/index.php)
   is that an application is a collection of services that when
   wired together, are an "application".  By asking the
   Application object to run() and send(), it will coordinate
   with all the other services in the service manager to do
   what is necessary to run() and send().

 * How do I create a service?  You write a factory object,
   then register through the Module in which it resides:

   * For example a DbAdapter: https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/DbAdapterFactory.php

   * And its registration:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L54

 * How do override a base service?  Such as the ViewResolver?

   * This can be achieved through the application.config.php file:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/config/application.config.php#L25

   * And the factory to produce this alternate ViewResolver:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/ViewResolverFactory.php

 * How Do I use Zend\Di during development and for my Application code?

   * Use it as normal.  If a service is not registered with the
     Service Manager it will use Di to create that service, for
     example, inside a controller:


https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Controller/IndexController.php#L13

     Since MovieFinder is not registered service, Zend\Di will
     produce this instance (and through reflection, determine
     that it needs to use a Db Adapter.)

 * But how does Zend\Di share instances with Zend\ServiceManager?

   * Easy, by telling Zend\ServiceManager that an alias exists
     for DbAdapter as "Zend\Db\Adapter\Adapter", when Di goes to
     look for this class, it will consult the ServiceManager for
     a service by that name (which happens to be the class name):


https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L57


== Long Term Benefits ==

Since Zend\ServiceManager Factories are a very concrete thing, Zend\Di can be fashioned in a way to create service factories for you.  It will do this since it already knows how things are created and wired together already.  It will take the information and create service factory classes.  This step will be a compilation step, and to draw a parallel, is similar in nature to how Doctrine needs to create Proxy classes.  In as sense, it is code generation.  This means that after compilation, Zend\Di will not need to be consulted as to how to build and wire an instance of something.

This could be exposed as either:

 * A button inside of a Developer toolbar
 * A command line script "compile-services" exposed via zf2.php perhaps

Will you need to do this?  No, since the majority of the infrastucture and shipped modules would (should) have their own Factories (instead of leveraging DI), things will already be as fast as they can be.  Zend\Di will be in place for the application developer to decide when and how to generate service factories.



Discuss!
-ralph





--

Taiwen Jiang (aka D.J.)

Build Xoops Engine
web and mobile application platform

CTO for EEFOCUS.com
Leading social platform for electronics professionals


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Marco Pivetta
@D. J. is there cases where you don't need application modules? :) (yeah, there are, but I guess that would be <5% of the cases, as Symfony2 already demonstrated).

Anyway, as I said, I like the implementation, and yes, it doesn't depend on Di right now (while I'm fine having Di depend on the ServiceManager).

I will keep working with Di configuration, hoping in the ServiceManager compiler. I really like its flexibility, and my only problem with it was performance. That would probably be solved :) (and please consider that I don't have problems with that kind of meta-programming until it has decent rules, exactly like normal programming).

Just a couple notes I've already told to Matthew and Ralph:

 1. Zend\Mvc\Application\Service\ServiceManagerConfiguration currently defines some aliases that are too short and imo should be stripped. I'd use abbreviations there only when they really bring some advantage :)
 2. I'd check if the new ServiceManager can load configuration from merged config (re-write it's own configuration after modules have been loaded?). This is currently achieved by `Zend\Module\Listener\ServiceListener`, which works. I just wonder if it should be moved to `loadModules.post` with high priority like the `Zend\Module\Listener\LocatorRegistrationListener` does, as setting a new factory means overwriting any previous available setting in a "destructive" way (can't get back). During `loadModules`, all modules could instead interact with the configurations provided for the service manager by the various modules, thus doing decisions about factories before those are set :)
 3. (thought about this one today) Mvc shouldn't depend on Di, but I'd just check if it is still possible to compile service factories instead of having some hardcoded in the framework directly (yes, I could always skip the ServiceManagerConfiguration construction). Just a weird idea, but heh, I'm the weirdo who likes giant config arrays, and compiling my config would still give me a good idea of why my `Di\Configuration` isn't working ;)

Anyway, you did an impressive work Ralph :) Congrats!

Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 25 April 2012 03:56, D. J. <[hidden email]> wrote:
The Zend\ServiceManager is good as long as it is not dependent on Zend\Di.
Actually we already build such a service manager in our new application framework which utilizes ZF2 as the library.

BTW, it would be appreciated if we first make the ZF2 framework itself solid before thinking of those so-called modules.
Make the ZF2 solid and ready as soon as possible, leave application modules to users who need modules.

Cheers!


On Wed, Apr 25, 2012 at 3:48 AM, Ralph Schindler <[hidden email]> wrote:
Hey Contributors,

There is no TL;DR for this email as this email is a _must-read_ for those developing ZF2 applications and modules.

== Background ==

Currently, Zend Framework 2 MVC based application infrastructure requires that Zend\Di play an integral part in the application setup/bootstrap and dispatching.  Zend\Di is one part complex one part magical for many individuals.  And, while it has many benefits in the areas of flexibility and service management, it does present some issues in the performance and complexity areas.

Even if we were to solve the performance problem with Zend\Di by "compiling definitions" (I actually don't think we'll see that much improvement by doing this), we still have the complexity issue.  At first sight, developers must, instead of writing code, write configuration (metadata) about code - and this, to me and some others, presents and immediate turn-off as a developer now has to familiarize themselves intimately with Zend\Di in order to start building real applications.  This is called Meta-Programming.

I'd like to solve this problem with a new component that will be the minimal dependency in the MVC stack, but we can also retain the benefits of Zend\Di.  The pattern at play is Service Location.  And while I've named this component a number of things in past incarnations: Zend\ServiceLocator and Zend\InstanceManager.. The final implementation of this component is called Zend\ServiceManager.

The name is chosen so that it mirrors other core components in the ZF2 life-cycle: Zend\EventManager and Zend\Module (I'd actually support renaming this to Zend\ModuleManager, but that is a separate issue.)

How does this solve our problems we have with Zend\Di?

 * First, it solves the problems of service sharing and service
   location though a simpler implementation

 * The Zend\ServiceManager implementation does not concern itself
   with auto-initialization and auto-wiring of instances and
   dependencies.

 * It provides developers a richer infrastructure to allow them
   to create service factories that do the service initialization
   and wiring of dependencies.  This is as opposed to Zend\Di's
   mantra of writing configuration that will influence an object
   which will create and wire instances and dependencies for you.

 * Which means there is no expensive configuration to class
   structure mapping that needs to happen in order to create
   a service/instance/object.

This does not mean that Zend\Di goes away, on the contrary.  Zend\Di is still wired as a default service of the MVC stack, and is also presented through Zend\ServiceManager as a fallback option when a defined service cannot be located.

In short, this allows consumers of ZF2 a more lightweight and faster infrastructure to build their applications on.  This also allows for usage of Zend\Di for RAD development and more complex service situations.

== How it Works ==

In Zend\ServiceManager, instances can be created primarily through objects/classes that either implement the FactoryInterface, or are closueres, or are callable.  These factories are attached to particular service name, like "DbAdapter", for example.  When someone calls $serviceManager->get('DbAdapter');, it will check to see if that instance was already created; if it wasn't, it will search for a factory to create that instance.  All factories/closures/callable have access to the ServiceManager to be able to resolve any dependencies, should they need to do this.

This is the basis of Service Location.

Other similar, but more lightweight implementations of this same principle can be explored in Fabien's Pimple (http://pimple.sensiolabs.org/ and also Tweetie http://twittee.org/.  In my mind, neither of these qualify as a DI Container as neither do "auto-instantiation" or "auto-wiring" of instances and dependencies ... these are really service location containers.

== Seeing it in Action ==

First, you should review the primary API of the ServiceManager here:

 * In my feature/service-manager branch:

https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/ServiceManager

Then, you should review how this is integrated into Zend\Mvc (same branch)

 * https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/Mvc

Next, have a look at the skeleton application that consumes both Zend\Mvc and Zend\ServiceManager:

 * Again, inside a feature/service-manager branch:

https://github.com/ralphschindler/ZendSkeletonApplication/tree/feature/service-manager

Things to look out for:

 * There is a factory per service, you can examine that inside
   the Zend\Mvc\Service namespace.  The view aspect of this can
   more than likely be reduced to simply a View service that
   has its dependencies composed in as each dependency really
   does not need to be a service of its own.

 * The general perspective (as demonstrated by the index.php
   file https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/public/index.php)
   is that an application is a collection of services that when
   wired together, are an "application".  By asking the
   Application object to run() and send(), it will coordinate
   with all the other services in the service manager to do
   what is necessary to run() and send().

 * How do I create a service?  You write a factory object,
   then register through the Module in which it resides:

   * For example a DbAdapter: https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/DbAdapterFactory.php

   * And its registration:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L54

 * How do override a base service?  Such as the ViewResolver?

   * This can be achieved through the application.config.php file:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/config/application.config.php#L25

   * And the factory to produce this alternate ViewResolver:

https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Service/ViewResolverFactory.php

 * How Do I use Zend\Di during development and for my Application code?

   * Use it as normal.  If a service is not registered with the
     Service Manager it will use Di to create that service, for
     example, inside a controller:


https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/src/Application/Controller/IndexController.php#L13

     Since MovieFinder is not registered service, Zend\Di will
     produce this instance (and through reflection, determine
     that it needs to use a Db Adapter.)

 * But how does Zend\Di share instances with Zend\ServiceManager?

   * Easy, by telling Zend\ServiceManager that an alias exists
     for DbAdapter as "Zend\Db\Adapter\Adapter", when Di goes to
     look for this class, it will consult the ServiceManager for
     a service by that name (which happens to be the class name):


https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/service-manager/module/Application/Module.php#L57


== Long Term Benefits ==

Since Zend\ServiceManager Factories are a very concrete thing, Zend\Di can be fashioned in a way to create service factories for you.  It will do this since it already knows how things are created and wired together already.  It will take the information and create service factory classes.  This step will be a compilation step, and to draw a parallel, is similar in nature to how Doctrine needs to create Proxy classes.  In as sense, it is code generation.  This means that after compilation, Zend\Di will not need to be consulted as to how to build and wire an instance of something.

This could be exposed as either:

 * A button inside of a Developer toolbar
 * A command line script "compile-services" exposed via zf2.php perhaps

Will you need to do this?  No, since the majority of the infrastucture and shipped modules would (should) have their own Factories (instead of leveraging DI), things will already be as fast as they can be.  Zend\Di will be in place for the application developer to decide when and how to generate service factories.



Discuss!
-ralph





--

Taiwen Jiang (aka D.J.)

Build Xoops Engine
web and mobile application platform

CTO for EEFOCUS.com
Leading social platform for electronics professionals



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

weierophinney
Administrator
In reply to this post by xoops
-- D. J. <[hidden email]> wrote
(on Wednesday, 25 April 2012, 09:56 AM +0800):
> The Zend\ServiceManager is good as long as it is not dependent on Zend\Di.

The point of the ServiceManager is to offer an _alternative_ to DI. That
said, the idea is that in the default MVC setup, we would provide a
fallback that uses a DI-enabled AbstractFactory. This provides the best
of both worlds, particularly since DI can help enable rapid application
development (you don't _need_ to define a service/factory for every
single class/component you use; DI will provide you with classes the
ServiceManager does not know about).

> Actually we already build such a service manager in our new application
> framework which utilizes ZF2 as the library.
>
> BTW, it would be appreciated if we first make the ZF2 framework itself solid
> before thinking of those so-called modules.
> Make the ZF2 solid and ready as soon as possible, leave application modules to
> users who need modules.

Modules are perhaps the most important new development in ZF2. They
allow us to finally achieve something we were never able to do in ZF1 --
an ecosystem for re-usable MVC code. Don't believe me?

Look at the vendor modules for my website:

    http://bit.ly/JzUy1j

vs the custom modules I developed:

    http://bit.ly/IbdUdd

Yep. Exactly ONE custom module; the rest are third-party.

Okay, not really, as I developed most of the third-party, vendor
modules, but the point is that I can drop them into any project I want,
and immediately have the functionality they offer available.

So, yes, we need to worry about modules. They are a key factor in the
future success of the framework.


> On Wed, Apr 25, 2012 at 3:48 AM, Ralph Schindler <[hidden email]>
> wrote:
>
>     Hey Contributors,
>
>     There is no TL;DR for this email as this email is a _must-read_ for those
>     developing ZF2 applications and modules.
>
>     == Background ==
>
>     Currently, Zend Framework 2 MVC based application infrastructure requires
>     that Zend\Di play an integral part in the application setup/bootstrap and
>     dispatching.  Zend\Di is one part complex one part magical for many
>     individuals.  And, while it has many benefits in the areas of flexibility
>     and service management, it does present some issues in the performance and
>     complexity areas.
>
>     Even if we were to solve the performance problem with Zend\Di by "compiling
>     definitions" (I actually don't think we'll see that much improvement by
>     doing this), we still have the complexity issue.  At first sight,
>     developers must, instead of writing code, write configuration (metadata)
>     about code - and this, to me and some others, presents and immediate
>     turn-off as a developer now has to familiarize themselves intimately with
>     Zend\Di in order to start building real applications.  This is called
>     Meta-Programming.
>
>     I'd like to solve this problem with a new component that will be the
>     minimal dependency in the MVC stack, but we can also retain the benefits of
>     Zend\Di.  The pattern at play is Service Location.  And while I've named
>     this component a number of things in past incarnations: Zend\ServiceLocator
>     and Zend\InstanceManager.. The final implementation of this component is
>     called Zend\ServiceManager.
>
>     The name is chosen so that it mirrors other core components in the ZF2
>     life-cycle: Zend\EventManager and Zend\Module (I'd actually support
>     renaming this to Zend\ModuleManager, but that is a separate issue.)
>
>     How does this solve our problems we have with Zend\Di?
>
>      * First, it solves the problems of service sharing and service
>        location though a simpler implementation
>
>      * The Zend\ServiceManager implementation does not concern itself
>        with auto-initialization and auto-wiring of instances and
>        dependencies.
>
>      * It provides developers a richer infrastructure to allow them
>        to create service factories that do the service initialization
>        and wiring of dependencies.  This is as opposed to Zend\Di's
>        mantra of writing configuration that will influence an object
>        which will create and wire instances and dependencies for you.
>
>      * Which means there is no expensive configuration to class
>        structure mapping that needs to happen in order to create
>        a service/instance/object.
>
>     This does not mean that Zend\Di goes away, on the contrary.  Zend\Di is
>     still wired as a default service of the MVC stack, and is also presented
>     through Zend\ServiceManager as a fallback option when a defined service
>     cannot be located.
>
>     In short, this allows consumers of ZF2 a more lightweight and faster
>     infrastructure to build their applications on.  This also allows for usage
>     of Zend\Di for RAD development and more complex service situations.
>
>     == How it Works ==
>
>     In Zend\ServiceManager, instances can be created primarily through objects/
>     classes that either implement the FactoryInterface, or are closueres, or
>     are callable.  These factories are attached to particular service name,
>     like "DbAdapter", for example.  When someone calls $serviceManager->get
>     ('DbAdapter');, it will check to see if that instance was already created;
>     if it wasn't, it will search for a factory to create that instance.  All
>     factories/closures/callable have access to the ServiceManager to be able to
>     resolve any dependencies, should they need to do this.
>
>     This is the basis of Service Location.
>
>     Other similar, but more lightweight implementations of this same principle
>     can be explored in Fabien's Pimple (http://pimple.sensiolabs.org/ and also
>     Tweetie http://twittee.org/.  In my mind, neither of these qualify as a DI
>     Container as neither do "auto-instantiation" or "auto-wiring" of instances
>     and dependencies ... these are really service location containers.
>
>     == Seeing it in Action ==
>
>     First, you should review the primary API of the ServiceManager here:
>
>      * In my feature/service-manager branch:
>
>     https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/
>     Zend/ServiceManager
>
>     Then, you should review how this is integrated into Zend\Mvc (same branch)
>
>      * https://github.com/ralphschindler/zf2/tree/feature/service-manager/
>     library/Zend/Mvc
>
>     Next, have a look at the skeleton application that consumes both Zend\Mvc
>     and Zend\ServiceManager:
>
>      * Again, inside a feature/service-manager branch:
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/tree/feature/
>     service-manager
>
>     Things to look out for:
>
>      * There is a factory per service, you can examine that inside
>        the Zend\Mvc\Service namespace.  The view aspect of this can
>        more than likely be reduced to simply a View service that
>        has its dependencies composed in as each dependency really
>        does not need to be a service of its own.
>
>      * The general perspective (as demonstrated by the index.php
>        file https://github.com/ralphschindler/ZendSkeletonApplication/blob/
>     feature/service-manager/public/index.php)
>        is that an application is a collection of services that when
>        wired together, are an "application".  By asking the
>        Application object to run() and send(), it will coordinate
>        with all the other services in the service manager to do
>        what is necessary to run() and send().
>
>      * How do I create a service?  You write a factory object,
>        then register through the Module in which it resides:
>
>        * For example a DbAdapter: https://github.com/ralphschindler/
>     ZendSkeletonApplication/blob/feature/service-manager/module/Application/src
>     /Application/Service/DbAdapterFactory.php
>
>        * And its registration:
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/
>     service-manager/module/Application/Module.php#L54
>
>      * How do override a base service?  Such as the ViewResolver?
>
>        * This can be achieved through the application.config.php file:
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/
>     service-manager/config/application.config.php#L25
>
>        * And the factory to produce this alternate ViewResolver:
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/
>     service-manager/module/Application/src/Application/Service/
>     ViewResolverFactory.php
>
>      * How Do I use Zend\Di during development and for my Application code?
>
>        * Use it as normal.  If a service is not registered with the
>          Service Manager it will use Di to create that service, for
>          example, inside a controller:
>
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/
>     service-manager/module/Application/src/Application/Controller/
>     IndexController.php#L13
>
>          Since MovieFinder is not registered service, Zend\Di will
>          produce this instance (and through reflection, determine
>          that it needs to use a Db Adapter.)
>
>      * But how does Zend\Di share instances with Zend\ServiceManager?
>
>        * Easy, by telling Zend\ServiceManager that an alias exists
>          for DbAdapter as "Zend\Db\Adapter\Adapter", when Di goes to
>          look for this class, it will consult the ServiceManager for
>          a service by that name (which happens to be the class name):
>
>
>     https://github.com/ralphschindler/ZendSkeletonApplication/blob/feature/
>     service-manager/module/Application/Module.php#L57
>
>
>     == Long Term Benefits ==
>
>     Since Zend\ServiceManager Factories are a very concrete thing, Zend\Di can
>     be fashioned in a way to create service factories for you.  It will do this
>     since it already knows how things are created and wired together already.
>      It will take the information and create service factory classes.  This
>     step will be a compilation step, and to draw a parallel, is similar in
>     nature to how Doctrine needs to create Proxy classes.  In as sense, it is
>     code generation.  This means that after compilation, Zend\Di will not need
>     to be consulted as to how to build and wire an instance of something.
>
>     This could be exposed as either:
>
>      * A button inside of a Developer toolbar
>      * A command line script "compile-services" exposed via zf2.php perhaps
>
>     Will you need to do this?  No, since the majority of the infrastucture and
>     shipped modules would (should) have their own Factories (instead of
>     leveraging DI), things will already be as fast as they can be.  Zend\Di
>     will be in place for the application developer to decide when and how to
>     generate service factories.
>
>
>
>     Discuss!
>     -ralph
>
>
>
>
>
>
> --
>
> Taiwen Jiang (aka D.J.)
>
> Build Xoops Engine
> http://www.xoopsengine.org
> web and mobile application platform
>
> CTO for EEFOCUS.com
> Leading social platform for electronics professionals
>
>

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

ralphschindler
In reply to this post by xoops
> BTW, it would be appreciated if we first make the ZF2 framework itself
> solid before thinking of those so-called modules.
> Make the ZF2 solid and ready as soon as possible, leave application
> modules to users who need modules.

Interestingly, when you check out the Skeleton application, the
"Application" namespace is a module, so in effect, when you start
writing applications, you too are writing modules.

It's important to remember that with ZF2 we are attempting to create an
infrastructure where the concept of a portable module can exist and
flourish.  This, in ZF1, was simply not possible.

So this particular component is (along with 2 other core components), is
the foundation of what will likely allow modules to flourish.

-ralph

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Artur Bodera
In reply to this post by weierophinney
On Wed, Apr 25, 2012 at 3:32 PM, Matthew Weier O'Phinney <[hidden email]> wrote:
The point of the ServiceManager is to offer an _alternative_ to DI. That
said, the idea is that in the default MVC setup, we would provide a
fallback that uses a DI-enabled AbstractFactory. This provides the best
of both worlds, particularly since DI can help enable rapid application
development (you don't _need_ to define a service/factory for every
single class/component you use; DI will provide you with classes the
ServiceManager does not know about).

The way I see ServiceManager now is a Zend_Registry on steroids :-)

There's nothing inherently wrong about it, but it's actually just something that calls factories
and keeps a registry. I don't see it as alternative to DI as it does not give the same benefits
as DI. One of the main drawbacks for me, is that using SM means going back to depending 
constructors/factories and depending on them, then manually handling dependencies.

It's not an alternative to DI, but a complementary component. It might be useful for some
cases, but its simplicity does not allow for i.e. auto instantiating dependencies.
Actually, SM looks very much like the ZF1 Application resources broker - remember calling
->bootstrap("foo")  which in turn called ->boostrap("bar") etc. ? It's basically the same thing.

I also fail to see how SM would replace DI (or be a drop-in replacement). Assuming Foo
depends on Bar, we need configuration for both of those. With DI, it's explicitly defined,
it's constructed using just a constructor (or fromArray() or other factory). That means that
now instead of construction (meta)info about components (which was fine and was easily
overriden in modules, or sub-modules etc.), now I have to have each dependency wrapped
in a factory, which calls SM and another factory etc. Not mentioning optimization nightmares
with that and total ambiguity (as those factories can be monkey-patched or generally
written poorly)...

DI is by nature meta-programming, but the complexity factor can be brought down in several
ways. SM is obviously easier to use (because everyone uses factories and constructors
anyways), but I feel we're loosing something along the way that was just starting to shine. 
Can't believe Ralph didn't twitch even once when endorsing SM :-)

Cheers.

-- 
      __
     /.)\   <a href="tel:%2B48%20695%20600%20936" value="+48695600936" target="_blank">+48 695 600 936
     \(./   [hidden email]


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

ralphschindler
In reply to this post by Marco Pivetta

> Anyway, as I said, I like the implementation, and yes, it doesn't depend
> on Di right now (while I'm fine having Di depend on the ServiceManager).

Service Manager doesn't depend on DI and visa-versa.  There is an
element of optional coupling that makes it so that Zend\Di can be used
as a factory for specific services or abstract services (think of
abstract services as a fall-through).

That code can be located here:

https://github.com/ralphschindler/zf2/tree/feature/service-manager/library/Zend/ServiceManager/Di

Basically there are 3 ways Di can be coupled in:

   * as a single service factory
   * as an abstract service factory
   * as a service "initializer"

Think of initializers as a place where an instance was created, but you
want to give something else the chance to "initialize" the object.

> Just a couple notes I've already told to Matthew and Ralph:
>
>   1. Zend\Mvc\Application\Service\ServiceManagerConfiguration currently
> defines some aliases that are too short and imo should be stripped. I'd
> use abbreviations there only when they really bring some advantage :)

Yep, we can discuss these, personally, I like them as they are very core
instance that will likely be used a lot.

>   2. I'd check if the new ServiceManager can load configuration from
> merged config (re-write it's own configuration after modules have been
> loaded?). This is currently achieved by

Currently, the ModuleManager merges the configurations and presents them
as a service called 'Configuration', before any modules are loaded.
This is ideal in that services provided by modules can have their
definitions (which would/should have been inside a module config) can be
overridden locally.  *Particularly* the target of specific aliases.
This is how modules can coordinate the sharing of services though
configured names.

> `Zend\Module\Listener\ServiceListener`, which works. I just wonder if it
> should be moved to `loadModules.post` with high priority like the
> `Zend\Module\Listener\LocatorRegistrationListener` does, as setting a
> new factory means overwriting any previous available setting in a
> "destructive" way (can't get back). During `loadModules`, all modules
> could instead interact with the configurations provided for the service
> manager by the various modules, thus doing decisions about factories
> before those are set :)

In general, modules should either be providing services to share, or
consuming services to share.  Ideally, you're not swapping out factories
of services because if a service was registered previously, that means
you are not the owner of that service.  What you should do, conversely,
is interact with that service (you're a consumer after all), and
configure it as per your needs.  Ideally, the service you are
interacting with has taken into account that it will have lots of
collaborators during its lifetime.

>   3. (thought about this one today) Mvc shouldn't depend on Di, but I'd
> just check if it is still possible to compile service factories instead
> of having some hardcoded in the framework directly (yes, I could always
> skip the ServiceManagerConfiguration construction). Just a weird idea,
> but heh, I'm the weirdo who likes giant config arrays, and compiling my
> config would still give me a good idea of why my `Di\Configuration`
> isn't working ;)

I'd agree with you there, when it comes down to it, Zend\Mvc is about
consuming implementations (not interfaces) that made it easy to build
applications.  That said, there are 3 main components that together make
up Zend\Mvc are the EventManager, ServiceManager and ModuleManager.

> Anyway, you did an impressive work Ralph :) Congrats!

Thanks!
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

ralphschindler
In reply to this post by Artur Bodera

> The way I see ServiceManager now is a Zend_Registry on steroids :-)

ServiceManager is an implementation of a Service Locator.  A registry
also a object container, but the difference is that the objects in the
registry are the things you are tracking.  A service locator, on the
other hand, tracks the things that it will utilize to find the proper
object.

ServiceManager is one part service locator and one part registry.

> There's nothing inherently wrong about it, but it's actually just
> something that calls factories
> and keeps a registry. I don't see it as alternative to DI as it does not
> give the same benefits
> as DI. One of the main drawbacks for me, is that using SM means going
> back to depending
> constructors/factories and depending on them, then manually handling
> dependencies.

You are right in a sense.  I don't call ServiceManager a replacement for
Di.  Basically what I've done is taken some of the aspects out of Di and
moved them into a separate component.  This basically means that the
roles and responsibilities have shifted:

   ServiceManager:
     * named instances
     * creating services based on factories/closures/invokables
     * registry services

   Di:
     * auto-instantiation
     * auto-wiring

> It's not an alternative to DI, but a complementary component. It might
> be useful for some

Exactly.

> DI is by nature meta-programming, but the complexity factor can be
> brought down in several
> ways. SM is obviously easier to use (because everyone uses factories and

In all honesty, unless something changed in ZF2, I'd have a hard time
justifying its usage in a project unless that project's size and scope
was significant.  Zend\Di, even for me, is too complex a component for
small and medium projects that have 1 to perhaps 2 developers on them.

To reduce the complexity of Zend\Di (which up till now is a requirement
of ZF2 based Skeleton apps), you'd have to introduce concepts that I
feel, are contrary to the PHP ecosystem: namely compiling and code
generation.  PHP is a dynamic language.

Furthermore, PHP is a shared-nothing architecture.  For Zend\Di, that
means that there is no opportunity (like in java) to persist expensive
objects in memory between requests.  That means that you have to have
compiling or code-generation to get to the level of simplicity and have
the level of performance improvements that make it worthwhile to have in
the first place.

> constructors
> anyways), but I feel we're loosing something along the way that was just
> starting to shine.
> Can't believe Ralph didn't twitch even once when endorsing SM :-)

The way I use Zend\Di vs. the way I see most people using Zend\Di is
completely different.  Most people are using Zend\Di to wire up every
object instance in their entire application.  I use Zend\Di to wire up
services that need coupling.  This is why Zend\Di (even with reflection)
is fast for me, but slow for everyone else.

Teaching concepts like services vs. instances, configuration vs.
dependencies, etc. is difficult when the great majority of PHP
developers are building application in the dynamic PHP sense, which
minimal to moderate understanding of OO best-practices.

It's simply easier to give someone a service manager and say: "go write
a closure that will instantiate the object you want, and oh, if you need
dependencies, then just ask the service locator for them and inject them
yourself.  Register that closure by a particular name and everyone else
will have access to it."

-ralph



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

jsmarshall
This post has NOT been accepted by the mailing list yet.
In reply to this post by Artur Bodera
Hi everyone. 

I prefer to keep quite and learn. I would like to give my input on this matter though because it could greatly affect how ZF2 is used or **if it is used at all**. To me it's clear that DI should not be the default setup for a web applications. Ralph has mentioned the limitations of php a few times and that is obviously why it is not suitable as the default mechanism. I imagine the main reason to implementing DI at all is for ZF2 and php to stay competitive in the _real_ enterprise sector. These applications would likely be served by a fully supported and optimized Zend Server. The majority of ZF2 users won't have this arrangement. 

> The way I see ServiceManager now is a Zend_Registry on steroids :-)

The majority of patterns are very similar and only differ slightly in the way delegation occurs. The name typically conveys the intent of the pattern. (In my experience, I am not an expect by any means).

> It's not an alternative to DI, but a complementary component.

Hair splitting is not productive. My bike shed is blue.

I am pretty sure that anyone building a web application requiring the power of DI is skilled enough to quickly reconfigure ZF2 in any way that they see fit. All I know is that before these changes it took > 800ms per page load and now it takes less than 200ms. I haven't experimented fully with the different setups for DI, but Ralph said he doesn't expect much improvement with compilation. I am excited about these changes.

Also the modules are awesome and evolving very nicely.

thanks,

jon


On Apr 25, 2012, at 10:54 AM, "Artur Bodera [via Zend Framework Community]" <[hidden email]> wrote:

On Wed, Apr 25, 2012 at 3:32 PM, Matthew Weier O'Phinney <[hidden email]> wrote:
The point of the ServiceManager is to offer an _alternative_ to DI. That
said, the idea is that in the default MVC setup, we would provide a
fallback that uses a DI-enabled AbstractFactory. This provides the best
of both worlds, particularly since DI can help enable rapid application
development (you don't _need_ to define a service/factory for every
single class/component you use; DI will provide you with classes the
ServiceManager does not know about).

The way I see ServiceManager now is a Zend_Registry on steroids :-)

There's nothing inherently wrong about it, but it's actually just something that calls factories
and keeps a registry. I don't see it as alternative to DI as it does not give the same benefits
as DI. One of the main drawbacks for me, is that using SM means going back to depending 
constructors/factories and depending on them, then manually handling dependencies.

It's not an alternative to DI, but a complementary component. It might be useful for some
cases, but its simplicity does not allow for i.e. auto instantiating dependencies.
Actually, SM looks very much like the ZF1 Application resources broker - remember calling
->bootstrap("foo")  which in turn called ->boostrap("bar") etc. ? It's basically the same thing.

I also fail to see how SM would replace DI (or be a drop-in replacement). Assuming Foo
depends on Bar, we need configuration for both of those. With DI, it's explicitly defined,
it's constructed using just a constructor (or fromArray() or other factory). That means that
now instead of construction (meta)info about components (which was fine and was easily
overriden in modules, or sub-modules etc.), now I have to have each dependency wrapped
in a factory, which calls SM and another factory etc. Not mentioning optimization nightmares
with that and total ambiguity (as those factories can be monkey-patched or generally
written poorly)...

DI is by nature meta-programming, but the complexity factor can be brought down in several
ways. SM is obviously easier to use (because everyone uses factories and constructors
anyways), but I feel we're loosing something along the way that was just starting to shine. 
Can't believe Ralph didn't twitch even once when endorsing SM :-)

Cheers.

-- 
      __
     /.)\   <a href="tel:%2B48%20695%20600%20936" value="+48695600936" target="_blank">+48 695 600 936
     \(./   [hidden email]





If you reply to this email, your message will be added to the discussion below:
http://zend-framework-community.634137.n4.nabble.com/Services-Instances-Dependencies-in-ZF2-tp4584632p4586837.html
To start a new topic under ZF Contributor, email [hidden email]
To unsubscribe from ZF Contributor, click here.
NAML
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Gregory
In reply to this post by ralphschindler
Hi Ralph,

Looks real nice.

Some minor observations or confusions:

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L187

Sorry for not trying it out yet, but can you explain what is happening there?

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L261

and

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/AbstractFactoryInterface.php#L7

It looks like the real class name should be passed to the factory and
not the canonicalized name?

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L273

$rName is supposed to be the real class name?

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L280

createThrowException is protect, should a setter be used/exist instead?

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L293

My initial concern was that it seems like that passing all newly
instantiated objects to all the initializers would mean a lot of
explicit testing of what they're initializing? Is this a workaround
for not injecting the ServiceManager if it implemented the Locator
interface (or so), or is the latter missing?

https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L378

$name is now $rName?

Any thoughts on how to get concrete method names for easier code
completion support, maybe an Application\ServiceManager?


Regards,


Greg
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Artur Bodera
In reply to this post by ralphschindler
On Wed, Apr 25, 2012 at 5:26 PM, Ralph Schindler <[hidden email]> wrote:

In all honesty, unless something changed in ZF2, I'd have a hard time justifying its usage in a project unless that project's size and scope was significant.  Zend\Di, even for me, is too 
complex a component for small and medium projects that have 1 to perhaps 2 developers on them.

I agree with that.

However keep in mind that DI was also our hope for a better way to configure those internal dependencies, sometimes going 4-5 levels in depth from the main (user-referenced) component in a _modular environment_. Because I'm still using ZF1 for a few projects, I've just recently tried to change some stuff in MVC. It took me 4 hours to dig through all loaders, brokers, instantiators etc. at which time I gave up - just because after subclassing nearly everything I had to subclass, I found a single hardcoded function deep inside MVC that I could not easily override. 

DI promised us easy way to substitute dependencies, configure them with a "bird's eye" perspective and I believe it's much better fit for modular stuff.

Even though your application is maintained by 1-2 developers, you might be (re-using) 10s of ZF2 modules from different vendors!  This gives you a total sum of a mid to large app, whether you like it or not (unless you write everything proprietary).

The more ambiguity (as in freedom of choice, configuration and factory implementation) we have here, the more possibly wrecked the module infrastructure will end up, and that will attract new wave of monkey patching (which kills the purpose of modular paradigm).
 

 
To reduce the complexity of Zend\Di (which up till now is a requirement of ZF2 based Skeleton apps), you'd have to introduce concepts that I feel, are contrary to the PHP ecosystem: namely compiling and code generation.  PHP is a dynamic language.
 
[...]
 
It's simply easier to give someone a service manager and say: "go write a closure that will instantiate the object you want, and oh, if you need dependencies, then just ask the service locator for them and inject them yourself.  Register that closure by a particular name and everyone else will have access to it."

Well, yeah. But again - modules, modules, modules. Tens of different people shipping different stuff you'd like to configure for your app, _without_ dwelling inside factories. Also - remember the ZF1 nightmare of Component::__construct(array( .... .. boatload of ambiguous options described in docs .... ) )


It was hard for me to swallow DI for the same reasons you've described, but once it's here (and the whole concept looks legit), now you're effectively scrapping the idea in favor of - factories with a registry :-) 

It is simple. It is easy to understand. It is fast to write. But is it any good for our modular-inf goals ?


-- 
      __
     /.)\   +48 695 600 936
     \(./   [hidden email]
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Marco Pivetta
@jon I'm with Artur all over the line.
Performance is a problem solved by compilation for me, and I see a LOT of problems in case modules start defining factories, over factories, over factories over factories (and then me defining other factories over those).

The modules as they are now are amazing with DIC config, and Artur covered the reasons quite well.
I will push hard to keep the DIC factory as always on in a standard ZF2 application environment. Instead, compilation can handle a lot of what people are currently fearing (performance degradation).

On the "difficulty" of DIC configuration: yes, I got quite some experience with Zend\Di, but it took me around half to 3/4 of an hour to explain it to my co-workers and everyone was like "WOAH!" (and they got doctrine working, so not just words!) (btw, thank you Ralph for this jewel!).

Getting some aid by tools (tools that check DIC validity, and here I need to talk with Arthur about that) would make that even easier.

I have already explained following simplified (yet wrong) thought about DIC vs SM on IRC some times:

Assuming we're comparing DIC and EM to the CSS (Cascading Style Sheets), DIC represent the "*", "div", ".class", "#id" in CSS, while IM represents only "#id" and "!important".

This is what I see at least.
So IM is indeed, as Artur said, an additional layer. We wanted it because we previously had to work with Zend\Di internals to do some hacks, not because it is a replacement for the features brought by DIC.
I still see Zend\Di as the main part of ALL my web applications, be them corporate or small sites.

Need performance? Then compile your stuff or define your factory on your own, but only where it is critical for performance.
 
There's another point I can add against IM as default way of acting, and it is how Symfony 2 currently handles DIC.
As the guys in SF2 don't do auto-wiring, module developers build some fat factories that build the services and couple them with some hardcoded identifiers (like `$locator->get('some.symfony.service.id')`).
I dislike the Symfony2 approach. It is valid, it works, but it makes all of your code locator aware as injections without autowiring are painful to setup (and developers are lazy). You would not only make your code locator aware, but you also don't have any warranty that your locator actually contains the services you need, nor that it uses the same identifiers to store them! Zend\Di is slow, yes, but it solves that problem (and I'd love to see it used in Symfony world too), and in a very elegant way.

So I would refrain from telling that the IM is "better than Zend\Di" right now. It brings more problems than the ones it has solved if it becomes the standard approach.




Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 25 April 2012 18:44, Artur Bodera <[hidden email]> wrote:
On Wed, Apr 25, 2012 at 5:26 PM, Ralph Schindler <[hidden email]> wrote:

In all honesty, unless something changed in ZF2, I'd have a hard time justifying its usage in a project unless that project's size and scope was significant.  Zend\Di, even for me, is too 
complex a component for small and medium projects that have 1 to perhaps 2 developers on them.

I agree with that.

However keep in mind that DI was also our hope for a better way to configure those internal dependencies, sometimes going 4-5 levels in depth from the main (user-referenced) component in a _modular environment_. Because I'm still using ZF1 for a few projects, I've just recently tried to change some stuff in MVC. It took me 4 hours to dig through all loaders, brokers, instantiators etc. at which time I gave up - just because after subclassing nearly everything I had to subclass, I found a single hardcoded function deep inside MVC that I could not easily override. 

DI promised us easy way to substitute dependencies, configure them with a "bird's eye" perspective and I believe it's much better fit for modular stuff.

Even though your application is maintained by 1-2 developers, you might be (re-using) 10s of ZF2 modules from different vendors!  This gives you a total sum of a mid to large app, whether you like it or not (unless you write everything proprietary).

The more ambiguity (as in freedom of choice, configuration and factory implementation) we have here, the more possibly wrecked the module infrastructure will end up, and that will attract new wave of monkey patching (which kills the purpose of modular paradigm).
 

 
To reduce the complexity of Zend\Di (which up till now is a requirement of ZF2 based Skeleton apps), you'd have to introduce concepts that I feel, are contrary to the PHP ecosystem: namely compiling and code generation.  PHP is a dynamic language.
 
[...]
 
It's simply easier to give someone a service manager and say: "go write a closure that will instantiate the object you want, and oh, if you need dependencies, then just ask the service locator for them and inject them yourself.  Register that closure by a particular name and everyone else will have access to it."

Well, yeah. But again - modules, modules, modules. Tens of different people shipping different stuff you'd like to configure for your app, _without_ dwelling inside factories. Also - remember the ZF1 nightmare of Component::__construct(array( .... .. boatload of ambiguous options described in docs .... ) )


It was hard for me to swallow DI for the same reasons you've described, but once it's here (and the whole concept looks legit), now you're effectively scrapping the idea in favor of - factories with a registry :-) 

It is simple. It is easy to understand. It is fast to write. But is it any good for our modular-inf goals ?


-- 
      __
     /.)\   <a href="tel:%2B48%20695%20600%20936" value="+48695600936" target="_blank">+48 695 600 936
     \(./   [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

ralphschindler
On 4/25/12 12:48 PM, Marco Pivetta wrote:
> @jon I'm with Artur all over the line.
> Performance is a problem solved by compilation for me, and I see a LOT
> of problems in case modules start defining factories, over factories,
> over factories over factories (and then me defining other factories over
> those).

Compiling what?
I've tried compiling all of the RuntimeDefinition information into
static array Definitions and it is still slow.

If we're not compiling Zend\Di Definitions, then we are compiling actual
service factories that will replace what Zend\Di would go and do inside
Di::newInstance().  If that is the case, then this bit of code is then
wrapped inside of a Zend\ServiceManager\FactoryInterface and called by
the service manager.

The idea is that it might have originated in Zend\Di, but then a tool
compiled it out into a service factory.  Once that is done, you're
skipping Zend\Di altogether, or at least until your re-compile it.

> The modules as they are now are amazing with DIC config, and Artur
> covered the reasons quite well.
> I will push hard to keep the DIC factory as always on in a standard ZF2
> application environment. Instead, compilation can handle a lot of what
> people are currently fearing (performance degradation).

There is still a DI instance in Zend\Mvc.  The thing is that the core
services will not be using Zend\Di to be instantiated and wired.  Those
are done by factory.  All of the new code you write and want to expose
as a service would by default utilize Zend\Di unless you write a factory
(or compile a factory) and register it with the Service Manager.

> On the "difficulty" of DIC configuration: yes, I got quite some
> experience with Zend\Di, but it took me around half to 3/4 of an hour to
> explain it to my co-workers and everyone was like "WOAH!" (and they got
> doctrine working, so not just words!) (btw, thank you Ralph for this
> jewel!).

For every developer that sits down with ZF and expects to get started
writing apps within the first 5 minutes they will likely fail due to the
initial complexity.  Most people don't have someone sitting next to them
ready to explain how good the solution is, and then all the steps
necessary to make it performant.


> Need performance? Then compile your stuff or define your factory on your
> own, but only where it is critical for performance.

So, slow and complex by default, but then faster only if you learn the
tools to make it faster?

> There's another point I can add against IM as default way of acting, and
> it is how Symfony 2 currently handles DIC.
> As the guys in SF2 don't do auto-wiring, module developers build some
> fat factories that build the services and couple them with some
> hardcoded identifiers (like `$locator->get('some.symfony.service.id
> <http://some.symfony.service.id>')`).
> I dislike the Symfony2 approach. It is valid, it works, but it makes all
> of your code locator aware as injections without autowiring are painful

I haven't looked closely at the implementation, but basically, if your
DiC doesn't do auto-instantiation or auto-wiring of your instances, its
not a DiC, it is a service locator.  If you write factories and register
those with a locator object, you have a service locator, not a DiC
container by all definitions (definitions meaning all implementations in
other languages) I've seen.

> So I would refrain from telling that the IM is "better than Zend\Di"
> right now. It brings more problems than the ones it has solved if it
> becomes the standard approach.

To be discussed in the meeting today.  As I mentioned, its not a
replacement for the auto-wiring and auto-instantiation, its a
lighter-weight implementation to be used for service sharing and service
validation, primarily so that modules can have better interaction when
it comes to resolving module dependencies.

-ralph




Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Marco Pivetta
On 25 April 2012 20:07, Ralph Schindler <[hidden email]> wrote:
On 4/25/12 12:48 PM, Marco Pivetta wrote:
@jon I'm with Artur all over the line.
Performance is a problem solved by compilation for me, and I see a LOT
of problems in case modules start defining factories, over factories,
over factories over factories (and then me defining other factories over
those).

Compiling what?
I've tried compiling all of the RuntimeDefinition information into static array Definitions and it is still slow.

Compiling actual service factories, yes...
 

If we're not compiling Zend\Di Definitions, then we are compiling actual service factories that will replace what Zend\Di would go and do inside Di::newInstance().  If that is the case, then this bit of code is then wrapped inside of a Zend\ServiceManager\FactoryInterface and called by the service manager.

Indeed.
 

The idea is that it might have originated in Zend\Di, but then a tool compiled it out into a service factory.  Once that is done, you're skipping Zend\Di altogether, or at least until your re-compile it.


You would still get back to Di when needed (as it happens with Di currently asking SM for instances).
 

The modules as they are now are amazing with DIC config, and Artur
covered the reasons quite well.
I will push hard to keep the DIC factory as always on in a standard ZF2
application environment. Instead, compilation can handle a lot of what
people are currently fearing (performance degradation).

There is still a DI instance in Zend\Mvc.  The thing is that the core services will not be using Zend\Di to be instantiated and wired.  Those are done by factory.  All of the new code you write and want to expose as a service would by default utilize Zend\Di unless you write a factory (or compile a factory) and register it with the Service Manager.


Yes, I just fear people want to get rid of it :P

 

On the "difficulty" of DIC configuration: yes, I got quite some
experience with Zend\Di, but it took me around half to 3/4 of an hour to
explain it to my co-workers and everyone was like "WOAH!" (and they got
doctrine working, so not just words!) (btw, thank you Ralph for this
jewel!).

For every developer that sits down with ZF and expects to get started writing apps within the first 5 minutes they will likely fail due to the initial complexity.  Most people don't have someone sitting next to them ready to explain how good the solution is, and then all the steps necessary to make it performant.


Noone should ever start writing apps in 5 minutes. I think that's the fast way of ending up with a mess :\ Noone even started writing Doctrine 2 apps in a day, but yet here we are, and D2 is more than popular because it's pattern works. Yes, I answer to every lost soul on #doctrine when I can, but yet it is great. Zend\Di can be too if used correctly! Instead of trying to shrink it, let's get more people involved!

 



Need performance? Then compile your stuff or define your factory on your
own, but only where it is critical for performance.

So, slow and complex by default, but then faster only if you learn the tools to make it faster?


I don't see a problem in having compiled factories when deploying stuff... My development environment is always 3 or 4 times slower than the staging/ci/production one, but I never had problems with that, nor with the fact that all configs are smashed together (inclusive passwords & co. If the discussion is about consistency you could tell me that I'm building security issues in my system) in APL instead of being read and validated from some giant XML :)
 
There's another point I can add against IM as default way of acting, and
it is how Symfony 2 currently handles DIC.
As the guys in SF2 don't do auto-wiring, module developers build some
fat factories that build the services and couple them with some
hardcoded identifiers (like `$locator->get('some.symfony.service.id
<http://some.symfony.service.id>')`).

I dislike the Symfony2 approach. It is valid, it works, but it makes all
of your code locator aware as injections without autowiring are painful

I haven't looked closely at the implementation, but basically, if your DiC doesn't do auto-instantiation or auto-wiring of your instances, its not a DiC, it is a service locator.  If you write factories and register those with a locator object, you have a service locator, not a DiC container by all definitions (definitions meaning all implementations in other languages) I've seen.


Yes, and people make usage of locators all over their code (and hardcoded IDs, not even constants) because Symfony\DependencyInjection does not have a good way of configuring it, or at least not that I know of.

 

So I would refrain from telling that the IM is "better than Zend\Di"
right now. It brings more problems than the ones it has solved if it
becomes the standard approach.

To be discussed in the meeting today.  As I mentioned, its not a replacement for the auto-wiring and auto-instantiation, its a lighter-weight implementation to be used for service sharing and service validation, primarily so that modules can have better interaction when it comes to resolving module dependencies.


Yes, fine with that, it is just a matter of educating new users. If tomorrow I will have to override every factory of every module whose developer thought being "reusable", then we would have gone (in my opinion) in the wrong direction :)
 
-ralph





Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

ralphschindler
In reply to this post by Artur Bodera
On 4/25/12 11:44 AM, Artur Bodera wrote:
> However keep in mind that DI was also our hope for a better way to
> configure those internal dependencies, sometimes going 4-5 levels in
> depth from the main (user-referenced) component in a _modular
> environment_. Because I'm still using ZF1 for a few projects, I've just
> recently tried to change some stuff in MVC. It took me 4 hours to dig
> through all loaders, brokers, instantiators etc. at which time I gave up
> - just because after subclassing nearly everything I had to subclass, I
> found a single hardcoded function deep inside MVC that I could not
> easily override.

That's not a problem of not having a DiC, that is a problem of poor
infrastructure and less than SOLID OO code.

Even without DI, you should always be able to swap out dependencies in
objects, DI does not make this more or less possible, it just does it
for you.

If you don't practice good DI in your own code, then Zend\Di certainly
is not going to be able to inject the dependency for you.


> DI promised us easy way to substitute dependencies, configure them with

*IF* you write DI capable code, which getting developer's on board with
injecting dependencies into objects is a hard enough task as is.

> a "bird's eye" perspective and I believe it's much better fit for
> modular stuff.
>
> Even though your application is maintained by 1-2 developers, you might
> be (re-using) 10s of ZF2 modules from different vendors!  This gives you
> a total sum of a mid to large app, whether you like it or not (unless
> you write everything proprietary).

The problem is that the original idea is that modules have a depedency
on the Di interface, which is simply get().  Well, the problem now is
that they expect the DI *implementation* to always be available to
process their large configuration file and magically produces object
instances.

Now, every module requires Zend\Di\Di as a dependency, which means
someone consuming a module is inherently required to consume Zend\Di and
all of its baggage.

> The more ambiguity (as in freedom of choice, configuration and factory
> implementation) we have here, the more possibly wrecked the module
> infrastructure will end up, and that will attract new wave of monkey
> patching (which kills the purpose of modular paradigm).

You're assuming the only way a module infrastructure will flourish is if
Zend\Di\Di is a requirement for all modules.  I don't think this is the
case.

> Well, yeah. But again - modules, modules, modules. Tens of different
> people shipping different stuff you'd like to configure for your app,
> _without_ dwelling inside factories. Also - remember the ZF1 nightmare

you're either "dwelling" inside a factory (that can as easily be
overwritten by another factory), or you're "dwelling" inside some large
di configuration array.

> of Component::__construct(array( .... .. boatload of ambiguous options
> described in docs .... ) )

Instead, now people have to go read every method for the parameter name
to know what its called in order to inject it.  Then theres the problem
when people write code where two parameters share the same name but have
different context.  The list of problems goes on and on when people
don't write SOLID OO code in the first place.

> It was hard for me to swallow DI for the same reasons you've described,
> but once it's here (and the whole concept looks legit), now you're
> effectively scrapping the idea in favor of - factories with a registry :-)

Not scrapping it, just taking the best parts and moving them into a
service manager, which is more light-wegith, and letting DI be an opt-in
feature of the base ZendSkeletonApplication.

> It is simple. It is easy to understand. It is fast to write. But is it
> any good for our modular-inf goals ?

I guess in this case, we simply have not captured the requirements for
modules well enough.  We should probably do that.

-ralph




Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

weierophinney
Administrator
In reply to this post by Gregory
-- Greg <[hidden email]> wrote
(on Wednesday, 25 April 2012, 11:29 AM -0500):
> Some minor observations or confusions:
>
> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L187
>
> Sorry for not trying it out yet, but can you explain what is happening there?

Once we have the service name, this checks to see if an alias exists for
that name, recursively looking through aliases until it has come to a
final, fully qualified service name. Think, "aliases of aliases"
resolution here.

> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L261
>
> and
>
> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/AbstractFactoryInterface.php#L7
>
> It looks like the real class name should be passed to the factory and
> not the canonicalized name?

No -- the factory may be able to handle the canonicalized name itself.
As an example, in the implementation in Ralph's branch, he has attached
a DI-enabled AbstractFactory as a "fallback" for when no service is
found. Since DI has a concept of aliases as well, we want to use the
canonicalized name, since it may resolve via an alias.

> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L273
>
> $rName is supposed to be the real class name?

No -- if you look earlier, line 200, $rName is the requested name before
resolution.

> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L280
>
> createThrowException is protect, should a setter be used/exist instead?

Not necessary; classes in the same inheritance tree have access to each
other's protected members.

> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L293
>
> My initial concern was that it seems like that passing all newly
> instantiated objects to all the initializers would mean a lot of
> explicit testing of what they're initializing? Is this a workaround
> for not injecting the ServiceManager if it implemented the Locator
> interface (or so), or is the latter missing?

No. Consider the DIC for a moment. Any class that implements an *Aware
interface gets automatically injected via the setter(s) the *Aware
interface(s) define(s). With the ServiceManager, you have to add wiring
to make that occur -- and this is what the Initializers are for. If you
look here, in the MVC code:

    https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/Mvc/Service/ServiceManagerConfiguration.php#L106-110

Ralph sets up a rule to inject the ServiceManager for any class
implementing ServiceManagerAwareInterface -- we could do similarly for
EventManagers as well. Basically, it's a way to automate dependency
injection without having it be specifically a part of the ServiceFactory
generating the instance.

>
> https://github.com/ralphschindler/zf2/blob/feature/service-manager/library/Zend/ServiceManager/ServiceManager.php#L378
>
> $name is now $rName?

Yeah, this looks like a bug. :)

> Any thoughts on how to get concrete method names for easier code
> completion support, maybe an Application\ServiceManager?

You don't have to use short names -- you can also register services
using fully qualified class names. (Doing this has the advantage that if
you have the DI-enabled AbstractFactory in place, you can then fallback
to DI for any class that doesn't have an associated service.)

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

weierophinney
Administrator
In reply to this post by Marco Pivetta
-- Marco Pivetta <[hidden email]> wrote
(on Wednesday, 25 April 2012, 07:48 PM +0200):
> @jon I'm with Artur all over the line.
> Performance is a problem solved by compilation for me,

As you and I have discussed over IRC, nobody at this point actually
knows what the benefits of compilation really are, as nobody has
compiled DI definitions yet. Yes, there's some minimal support for it,
but there's no scaffolding available yet to make it easy, nor to make it
easy to provide a list of compiled definitions to the DIC to consume. As
such, any discussion about the benefits of compilation with regards to
the DIC are mere speculation.

I remember trying to do compilation sometime late last year, and the
results I encountered were not great. That said, I was only compiling a
subset of the classes used, but the point remains: until somebody can
write the tools and infrastructure to make this happen, it's all
speculation.

<snip>

> So IM is indeed, as Artur said, an additional layer. We wanted it because we
> previously had to work with Zend\Di internals to do some hacks,

I'd argue that there's a lot of stuff in our DI implementation that
should not be, and which provides additional performance overhead.
Technically speaking, DI should only be concerned about _object_
dependencies, not configuration values. However, we've added a lot of
features around the latter -- which has led to a lot of convoluted DI
configuration, folks complaining about case sensitivity of configuration
keys (one of our original goals for ZF2 was to remove configuration key
case sensitivity!!!!), and more.

As an example, if I have a configurable value with a setter, I have to
do the following to be able to inject it:

    return array('di' => array(
        'definition' => array('class' => array(
            'Foo\Bar\Baz' => array(
                'setBat' => array(
                    'bat' => array(
                        'type'     => false, // this allows it to accept non-objects
                        'required' => true,
                    ),
                ),
            ),
        )),
        'instance' => array(
            'Foo\Bar\Baz' => array('parameters' => array(
                'bat' => 'some arbitrary value',
            )),
        ),
    ));

This requires a huge understanding of how the DI container works, its
limitations, and special magic values you can use to influence behavior.

Additionally, the above doesn't demonstrate another issue: to prevent
collisions, you have to ensure all parameter names across all injection
methods are named _differently_ -- otherwise, the same value will be
injected across all injection points that have the same named parameter,
whether you like it or not. This will not work necessarily with legacy
code.

My point, the tl;dr of this section, is simply that we're abusing DI in
a lot of ways. I think the SM offers a nice complement, and also allows
us to separate some of the concerns in a better fashion. I definitely
love a ton about our DIC, but I can't argue that it's (a) easy to learn,
or (b) the right tool for all object creation.

<snip>

> There's another point I can add against IM as default way of acting, and it is
> how Symfony 2 currently handles DIC.
> As the guys in SF2 don't do auto-wiring, module developers build some fat
> factories that build the services and couple them with some hardcoded
> identifiers (like `$locator->get('some.symfony.service.id')`).

I agree here, actually. I'd argue that we should recommend using the
class names whenever possible, and resorting to aliases only to allow
multiple instances or alternative methods for creation.

My main points are:

 * The single loudest criticism I've heard of ZF2 is that it's too
   complex, and most of that criticism is addressed towards the DIC.

 * We should not target the default solution for DI or instance management
   only at senior developers; doing so restricts our usage and growth as
   a project.

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Marco Pivetta
This post has NOT been accepted by the mailing list yet.
I feel the pain about complexity.
I'll then just suggest to create some guide about best practices when it comes to "advanced" (reusable modules). If the goal here is to aid the end-user (the one who doesn't re-distribute modules) then fine. I agree, SM is probably easier.
But I just wish could not end with lots of potentially broken module compatibilities because people always go the easy way. So we should probably start separating guides about writing "reusable modules" and "your application".

Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 25 April 2012 21:37, weierophinney [via Zend Framework Community] <[hidden email]> wrote:
-- Marco Pivetta <[hidden email]> wrote
(on Wednesday, 25 April 2012, 07:48 PM +0200):
> @jon I'm with Artur all over the line.
> Performance is a problem solved by compilation for me,

As you and I have discussed over IRC, nobody at this point actually
knows what the benefits of compilation really are, as nobody has
compiled DI definitions yet. Yes, there's some minimal support for it,
but there's no scaffolding available yet to make it easy, nor to make it
easy to provide a list of compiled definitions to the DIC to consume. As
such, any discussion about the benefits of compilation with regards to
the DIC are mere speculation.

I remember trying to do compilation sometime late last year, and the
results I encountered were not great. That said, I was only compiling a
subset of the classes used, but the point remains: until somebody can
write the tools and infrastructure to make this happen, it's all
speculation.

<snip>

> So IM is indeed, as Artur said, an additional layer. We wanted it because we
> previously had to work with Zend\Di internals to do some hacks,

I'd argue that there's a lot of stuff in our DI implementation that
should not be, and which provides additional performance overhead.
Technically speaking, DI should only be concerned about _object_
dependencies, not configuration values. However, we've added a lot of
features around the latter -- which has led to a lot of convoluted DI
configuration, folks complaining about case sensitivity of configuration
keys (one of our original goals for ZF2 was to remove configuration key
case sensitivity!!!!), and more.

As an example, if I have a configurable value with a setter, I have to
do the following to be able to inject it:

    return array('di' => array(
        'definition' => array('class' => array(
            'Foo\Bar\Baz' => array(
                'setBat' => array(
                    'bat' => array(
                        'type'     => false, // this allows it to accept non-objects
                        'required' => true,
                    ),
                ),
            ),
        )),
        'instance' => array(
            'Foo\Bar\Baz' => array('parameters' => array(
                'bat' => 'some arbitrary value',
            )),
        ),
    ));

This requires a huge understanding of how the DI container works, its
limitations, and special magic values you can use to influence behavior.

Additionally, the above doesn't demonstrate another issue: to prevent
collisions, you have to ensure all parameter names across all injection
methods are named _differently_ -- otherwise, the same value will be
injected across all injection points that have the same named parameter,
whether you like it or not. This will not work necessarily with legacy
code.

My point, the tl;dr of this section, is simply that we're abusing DI in
a lot of ways. I think the SM offers a nice complement, and also allows
us to separate some of the concerns in a better fashion. I definitely
love a ton about our DIC, but I can't argue that it's (a) easy to learn,
or (b) the right tool for all object creation.

<snip>

> There's another point I can add against IM as default way of acting, and it is
> how Symfony 2 currently handles DIC.
> As the guys in SF2 don't do auto-wiring, module developers build some fat
> factories that build the services and couple them with some hardcoded
> identifiers (like `$locator->get('some.symfony.service.id')`).

I agree here, actually. I'd argue that we should recommend using the
class names whenever possible, and resorting to aliases only to allow
multiple instances or alternative methods for creation.

My main points are:

 * The single loudest criticism I've heard of ZF2 is that it's too
   complex, and most of that criticism is addressed towards the DIC.

 * We should not target the default solution for DI or instance management
   only at senior developers; doing so restricts our usage and growth as
   a project.

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]

If you reply to this email, your message will be added to the discussion below:
http://zend-framework-community.634137.n4.nabble.com/Services-Instances-Dependencies-in-ZF2-tp4584632p4587820.html
To start a new topic under ZF Contributor, email [hidden email]
To unsubscribe from ZF Contributor, click here.
NAML

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Services/Instances/Dependencies in ZF2

Sascha-Oliver Prolic
In reply to this post by weierophinney
Am 25. April 2012 21:37 schrieb Matthew Weier O'Phinney <[hidden email]>:

> -- Marco Pivetta <[hidden email]> wrote
> (on Wednesday, 25 April 2012, 07:48 PM +0200):
>> @jon I'm with Artur all over the line.
>> Performance is a problem solved by compilation for me,
>
> As you and I have discussed over IRC, nobody at this point actually
> knows what the benefits of compilation really are, as nobody has
> compiled DI definitions yet. Yes, there's some minimal support for it,
> but there's no scaffolding available yet to make it easy, nor to make it
> easy to provide a list of compiled definitions to the DIC to consume. As
> such, any discussion about the benefits of compilation with regards to
> the DIC are mere speculation.
>
> I remember trying to do compilation sometime late last year, and the
> results I encountered were not great. That said, I was only compiling a
> subset of the classes used, but the point remains: until somebody can
> write the tools and infrastructure to make this happen, it's all
> speculation.
>
> <snip>
>
>> So IM is indeed, as Artur said, an additional layer. We wanted it because we
>> previously had to work with Zend\Di internals to do some hacks,
>
> I'd argue that there's a lot of stuff in our DI implementation that
> should not be, and which provides additional performance overhead.
> Technically speaking, DI should only be concerned about _object_
> dependencies, not configuration values. However, we've added a lot of
> features around the latter -- which has led to a lot of convoluted DI
> configuration, folks complaining about case sensitivity of configuration
> keys (one of our original goals for ZF2 was to remove configuration key
> case sensitivity!!!!), and more.
>
> As an example, if I have a configurable value with a setter, I have to
> do the following to be able to inject it:
>
>    return array('di' => array(
>        'definition' => array('class' => array(
>            'Foo\Bar\Baz' => array(
>                'setBat' => array(
>                    'bat' => array(
>                        'type'     => false, // this allows it to accept non-objects
>                        'required' => true,
>                    ),
>                ),
>            ),
>        )),
>        'instance' => array(
>            'Foo\Bar\Baz' => array('parameters' => array(
>                'bat' => 'some arbitrary value',
>            )),
>        ),
>    ));
>
> This requires a huge understanding of how the DI container works, its
> limitations, and special magic values you can use to influence behavior.
>
> Additionally, the above doesn't demonstrate another issue: to prevent
> collisions, you have to ensure all parameter names across all injection
> methods are named _differently_ -- otherwise, the same value will be
> injected across all injection points that have the same named parameter,
> whether you like it or not. This will not work necessarily with legacy
> code.
>
> My point, the tl;dr of this section, is simply that we're abusing DI in
> a lot of ways. I think the SM offers a nice complement, and also allows
> us to separate some of the concerns in a better fashion. I definitely
> love a ton about our DIC, but I can't argue that it's (a) easy to learn,
> or (b) the right tool for all object creation.
>
> <snip>
>
>> There's another point I can add against IM as default way of acting, and it is
>> how Symfony 2 currently handles DIC.
>> As the guys in SF2 don't do auto-wiring, module developers build some fat
>> factories that build the services and couple them with some hardcoded
>> identifiers (like `$locator->get('some.symfony.service.id')`).
>
> I agree here, actually. I'd argue that we should recommend using the
> class names whenever possible, and resorting to aliases only to allow
> multiple instances or alternative methods for creation.
>
> My main points are:
>
>  * The single loudest criticism I've heard of ZF2 is that it's too
>   complex, and most of that criticism is addressed towards the DIC.
>
>  * We should not target the default solution for DI or instance management
>   only at senior developers; doing so restricts our usage and growth as
>   a project.
>
> --
> Matthew Weier O'Phinney
> Project Lead            | [hidden email]
> Zend Framework          | http://framework.zend.com/
> PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc

Hi all,

First of all, my english is not so good, but i am trying my best to
express myself. I know that I am not that famous as Ralph and Matthew,
so chances are, that I will not get heard the same way, but anyways, I
think need to step in here.
The current decision to switch the di for servicemanager is something
I really can't understand.
I took a look in the zf2 wiki:
http://framework.zend.com/wiki/display/ZFDEV2/Zend+DI+QuickStart#ZendDIQuickStart-GeneratingServiceLocators
what happend with the old plannings?

// begin code

use Zend\Di\ServiceLocator\Generator;

// $di is a fully configured DI instance
$generator = new Generator($di);

$generator->setNamespace('Application')
          ->setContainerClass('Context');
$file = $generator->getCodeGenerator();
$file->setFilename(__DIR__ . '/../Application/Context.php');
$file->write();

// end code

This is basically the approach I followed the last 6 month since I
played the first time with Zend\Di. Let me explain a bit further
please.

First of all, I see Zend\Di\Definition\RuntimeDefinition a bit
critical. Well as it helps during development (I don't have to wire
all dependencies with array / builder definition, instead I can let
the RuntimeDefinition all the work) it is a bit kind of a blocker for
generating the service locator from di.

Artur Bodera explained pretty well, why the replacement of Zend\Di
with Zend\ServiceManager is a bad thing for the modularity expected
from zf2. I totally agree on that.

So here is my proposal:

1) Get rid of RuntimeDefinition (or use it only during development, I
would NEVER recomment to use it).
2) Use Array- or Builder-Definition instead. Compiled definitions are
also nice. But I don't like to use it in production.
3) Use a service locator generator, that is able to generate the
service locator from given di definition.
4) change index.php the way, that is tries to instantiate the
"Application\Context.php" (the generated service locator) and if it is
not found, use zend\di with the definition provided by the modules
instead.

I know the standard arguments that will be used against my proposal,
so I want to answer directly before getting asked for it:
a) Developers are lazy, they don't want to do meta programming.
b) Junior developers and people trying to play with zf the first time,
will get shocked and they probably don't want to use zf2 if they see
there is a need for such a complex di wiring through configuration.
c) Zend\Di is slow, even with compiled definitions.

So my answers to these questions are:
to a and b)
The last six month I led a team of 4 developers (without me, so 5 in
total). One person had a lot of experience with zf1. one person has
played with zf1 a few days. the other 2 guys didn't even know that
there is a zend framework at all (totally newbies). I integrated a
service locator generator (based on the ideas I got from
http://framework.zend.com/wiki/display/ZFDEV2/Zend+DI+QuickStart#ZendDIQuickStart-GeneratingServiceLocators)
which works the following way:
1) define all wiring in configuration. that's a lot of meta
programming, but it didn't cost us much time.
2) whenever someone changed a single line in the di configuration, he
needed to go into shell and type something like: "php sl_generator.php
--environment=development", that's all, a new service locator was
generated in a second.
3) Go ahead, visit your website.
The process was very clear. Nobody ever complained about generating
the service locator several times a day. Everybody, experienced
developers as well as juniors, who hadn't even an idea how a mvc stack
works, knew how to write the di configuration. very very easy, nobody
was lazy enough not to write the config. So when someone says,
programmers are lazy and they don't want to do that meta
programming... i have to disagree. that's just speculation, i made the
experience that everybody was like "WOAH, AWESOME" when they saw my
service locator generator, nobody seamed to be lazy.

to c)
Well I don't want to use compiled definitions, there a not that much
faster than RuntimeDefinition. Use a service locator generator
instead, that is able to generate the application context from a di
definition.

At last I want to answer matthew on his last post:

> This requires a huge understanding of how the DI container works, its
> limitations, and special magic values you can use to influence behavior.
>
> Additionally, the above doesn't demonstrate another issue: to prevent
> collisions, you have to ensure all parameter names across all injection
> methods are named _differently_ -- otherwise, the same value will be
> injected across all injection points that have the same named parameter,
> whether you like it or not. This will not work necessarily with legacy
> code.

1) As told above: I teached the understanding to junior developers,
everybody understood, with some code review I could also get the
people to write di enabled code. That's up to the team lead
(architect, whatever) of a development team.
2) In my own Service Locator generator implementation I don't have the
issue that all parameters names across all injection methods are named
diffrently. I used a litte different array structure to define that.

Last but not least:
Currently I don't have the service locator generator on github, but to
demonstrate the advantages, I pastebinned two examples:
a) [URL: http://pastebin.com/A6iTcu4k ] - It shows how to configure
doctrine orm with gedmo doctrine extensions.
b) [URL: http://pastebin.com/vEjytr7X ] - It shows what will be
generated from that config.
(Sorry for typos, missing values, etc. - could be that I missed some
points, I copy and pasted it from different config files and merged it
to one, to better show the benefits).

I will put the Service Locator generator on github the next days.

Conclusion: I can wire complete doctrine orm (even without that much
extra classes as written in DoctrineModule and DoctrineORMModule, @see
https://github.com/doctrine/DoctrineORMModule,
https://github.com/doctrine/DoctrineModule). I only needed to subclass
Doctrine\Common\EventManager and
Doctrine\ORM\Mapping\Driver\DriverChain.

We do NOT NEED a DI-implementation. We only need an array config,
which defines everything, and a service locator generator.

So I hope I will get some feedback on this email.

Best Regards

Sascha-Oliver Prolic
12
Loading...