|
I have been Reading Cal Evan's Guide to Zend Framework Programming.
In it he describes a Globals.php file for creating a single class to encapsulate access to global resources like the database connection, cache connection, config, etc. This seems useful, particularly if you use it in conjunction with his Controller_Request_Cli class for exposing some of your Zend-based application via command line or cron jobs. I was wondering if this is the recommended approach to exposing global resources or is there a Zend Framework "approved" method? - Steve W. |
|
Zend_Registry (http://framework.zend.com/manual/en/zend.registry.html) "is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage."
---- Jeremy Brown, Zend Certified Engineer (ZCE) Senior Web Developer Spear One 972.661.6038 www.spearone.com -----Original Message----- From: swilhelm [mailto:[hidden email]] Sent: Friday, January 09, 2009 2:27 PM To: [hidden email] Subject: [fw-general] Is Cal Evan's Globals.php a recommended approach I have been Reading Cal Evan's Guide to Zend Framework Programming. In it he describes a Globals.php file for creating a single class to encapsulate access to global resources like the database connection, cache connection, config, etc. This seems useful, particularly if you use it in conjunction with his Controller_Request_Cli class for exposing some of your Zend-based application via command line or cron jobs. I was wondering if this is the recommended approach to exposing global resources or is there a Zend Framework "approved" method? - Steve W. -- View this message in context: http://www.nabble.com/Is-Cal-Evan%27s-Globals.php-a-recommended-approach-tp21379960p21379960.html Sent from the Zend Framework mailing list archive at Nabble.com. |
|
Eh, That's not really what he meant.
You (the original poster) may want to take a look at the proposal that is in the works for Zend_Application. <http://framework.zend.com/wiki/display/ZFPROP/Zend_Application+-+Ben+Scholzen> I have been using a Bootstrap/Application class in a couple of projects which I have on the go right now, they can be invaluable for environment specific setup, as you mentioned. Jeremy Brown wrote: > Zend_Registry (http://framework.zend.com/manual/en/zend.registry.html) "is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage." > > ---- > Jeremy Brown, Zend Certified Engineer (ZCE) > Senior Web Developer > Spear One > 972.661.6038 > www.spearone.com > > > -----Original Message----- > From: swilhelm [mailto:[hidden email]] > Sent: Friday, January 09, 2009 2:27 PM > To: [hidden email] > Subject: [fw-general] Is Cal Evan's Globals.php a recommended approach > > > I have been Reading Cal Evan's Guide to Zend Framework Programming. > > In it he describes a Globals.php file for creating a single class to > encapsulate access to global resources like the database connection, cache > connection, config, etc. > > This seems useful, particularly if you use it in conjunction with his > Controller_Request_Cli class for exposing some of your Zend-based > application via command line or cron jobs. > > I was wondering if this is the recommended approach to exposing global > resources or is there a Zend Framework "approved" method? > > - Steve W. > -- > View this message in context: http://www.nabble.com/Is-Cal-Evan%27s-Globals.php-a-recommended-approach-tp21379960p21379960.html > Sent from the Zend Framework mailing list archive at Nabble.com. > |
|
In my bootstrap process, via a few plugins, I also setup the application environment bases upon settings in a config.xml file. These 'global' resources (db connection, environment class, etc) are then stored in the registry so that their values and functionality can be accessed later. How do you make your db connection available to other sections of your code once it has been established?
-----Original Message----- From: Jason Webster [mailto:[hidden email]] Sent: Friday, January 09, 2009 3:33 PM To: Zend Framework - General Subject: Re: [fw-general] Is Cal Evan's Globals.php a recommended approach Eh, That's not really what he meant. You (the original poster) may want to take a look at the proposal that is in the works for Zend_Application. <http://framework.zend.com/wiki/display/ZFPROP/Zend_Application+-+Ben+Scholzen> I have been using a Bootstrap/Application class in a couple of projects which I have on the go right now, they can be invaluable for environment specific setup, as you mentioned. Jeremy Brown wrote: > Zend_Registry (http://framework.zend.com/manual/en/zend.registry.html) "is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage." > > ---- > Jeremy Brown, Zend Certified Engineer (ZCE) > Senior Web Developer > Spear One > 972.661.6038 > www.spearone.com > > > -----Original Message----- > From: swilhelm [mailto:[hidden email]] > Sent: Friday, January 09, 2009 2:27 PM > To: [hidden email] > Subject: [fw-general] Is Cal Evan's Globals.php a recommended approach > > > I have been Reading Cal Evan's Guide to Zend Framework Programming. > > In it he describes a Globals.php file for creating a single class to > encapsulate access to global resources like the database connection, cache > connection, config, etc. > > This seems useful, particularly if you use it in conjunction with his > Controller_Request_Cli class for exposing some of your Zend-based > application via command line or cron jobs. > > I was wondering if this is the recommended approach to exposing global > resources or is there a Zend Framework "approved" method? > > - Steve W. > -- > View this message in context: http://www.nabble.com/Is-Cal-Evan%27s-Globals.php-a-recommended-approach-tp21379960p21379960.html > Sent from the Zend Framework mailing list archive at Nabble.com. > |
|
In reply to this post by swilhelm
I haven't seen this Globals.php but it sounds something similar to what I use myself.
To simplify access to those resources I created a wrapper around Zend_Registry, which handles lazy loading and returns preconfigured components. So all it takes is one line Foo::db()->fetchAll(...) to run some sql. And this works across multiple applications, web scripts and cron jobs. I think it's pretty good solution and it proved itself in many applications I built. Cheers
|
|
Administrator
|
In reply to this post by swilhelm
-- swilhelm <[hidden email]> wrote
(on Friday, 09 January 2009, 12:26 PM -0800): > > I have been Reading Cal Evan's Guide to Zend Framework Programming. > > In it he describes a Globals.php file for creating a single class to > encapsulate access to global resources like the database connection, cache > connection, config, etc. > > This seems useful, particularly if you use it in conjunction with his > Controller_Request_Cli class for exposing some of your Zend-based > application via command line or cron jobs. > > I was wondering if this is the recommended approach to exposing global > resources or is there a Zend Framework "approved" method? I've had some back and forth with Cal on his Globals class. :) I personally feel that this is primarily the realm of a registry or dependency injection; a class of static methods is typically difficult to test against, and makes it more difficult to determine what the actual dependencies are for classes that pull from it. Zend_Application may very well make such a Globals class obsolete, as it will make it easier to handle your dependencies and push them either into the registry or directly into the objects that need them. -- Matthew Weier O'Phinney Software Architect | [hidden email] Zend Framework | http://framework.zend.com/ |
|
Hi Matthew,
Yes, we've had this discussion before. :) When I wrote the first edition of the book I believe it was a good practice. Now however, I believe that there are better ways to accomplish the goals of Globals.php. The goals of Globals.php were 2 fold. 1) A central repository for application level variables 2) Lazy loading for certain classes like DB and Cache. Registry does indeed handle #1 nicely and I'll be using that in the second edition. As you know (because you showed me how) I've also solved the DB problem. The only piece I've not yet completed is the lazy loading of caching. I think a similar solution to DB will work, I've just not done it yet. :) I'm anxiously awaiting Zend_Application. That could change everything. =C= On Sat, Jan 10, 2009 at 5:31 AM, Matthew Weier O'Phinney <[hidden email]> wrote: -- swilhelm <[hidden email]> wrote |
|
In reply to this post by weierophinney
the registry is global too and it has no type checking, which makes a global class that has setter and getter for specific types winner over a general registry in my opinion. static classes that contain objects and are used inside dynamic objects are always an obstacle to testing, singletons more than registries but the problem does not disappear. a registry is nothing different than a "global $arrayWithObjects;" inside a function, just object oriented. The recommended approach (that is independend of Zend or whatnot) should always be explicit dependency injection. greetings, Benjamin On Fri, 9 Jan 2009 23:31:38 -0500, Matthew Weier O'Phinney <[hidden email]> wrote: > -- swilhelm <[hidden email] > wrote > (on Friday, 09 January 2009, 12:26 PM -0800): >> >> I have been Reading Cal Evan's Guide to Zend Framework Programming. >> >> In it he describes a Globals.php file for creating a single class to >> encapsulate access to global resources like the database connection, > cache >> connection, config, etc. >> >> This seems useful, particularly if you use it in conjunction with his >> Controller_Request_Cli class for exposing some of your Zend-based >> application via command line or cron jobs. >> >> I was wondering if this is the recommended approach to exposing global >> resources or is there a Zend Framework "approved" method? > > I've had some back and forth with Cal on his Globals class. :) > > I personally feel that this is primarily the realm of a registry or > dependency injection; a class of static methods is typically difficult > to test against, and makes it more difficult to determine what the > actual dependencies are for classes that pull from it. > > Zend_Application may very well make such a Globals class obsolete, as it > will make it easier to handle your dependencies and push them either > into the registry or directly into the objects that need them. > > -- > Matthew Weier O'Phinney > Software Architect | [hidden email] > Zend Framework | http://framework.zend.com/ |
|
To echo Benjamin, any scenario where static classes are called within another class open the door to problems with testing, portability and subclassing. It's for this reason it's always a good idea to minimise their use and at a minimum isolate the usage in central locations. Avoiding them is the most ideal situation.
The better solution is dependency injection, and one facet of this is removing explicit references to Registry/global items from source code. This is a feature the ZF has yet to include but Bradley Holt has ressurected the idea in the form of Zend_Container ( http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-+Bradley+Holt ) and I really really hope it, or an evolution of it (Zend_Application plugin?), makes it into the library. The last time something like this was proposed it was bogged down by ever expanding requirements and complexity - simpler is better. Paddy http://blog.astrumfutura.com http://www.survivethedeepend.com OpenID Europe Foundation Irish Representative From: Benjamin Eberlei <[hidden email]> To: [hidden email] Sent: Saturday, January 10, 2009 7:03:47 PM Subject: Re: [fw-general] Is Cal Evan's Globals.php a recommended approach the registry is global too and it has no type checking, which makes a global class that has setter and getter for specific types winner over a general registry in my opinion. static classes that contain objects and are used inside dynamic objects are always an obstacle to testing, singletons more than registries but the problem does not disappear. a registry is nothing different than a "global $arrayWithObjects;" inside a function, just object oriented. The recommended approach (that is independend of Zend or whatnot) should always be explicit dependency injection. greetings, Benjamin On Fri, 9 Jan 2009 23:31:38 -0500, Matthew Weier O'Phinney <[hidden email]> wrote: > -- swilhelm <[hidden email] > wrote > (on Friday, 09 January 2009, 12:26 PM -0800): >> >> I have been Reading Cal Evan's Guide to Zend Framework Programming. >> >> In it he describes a Globals.php file for creating a single class to >> encapsulate access to global resources like the database connection, > cache >> connection, config, etc. >> >> This seems useful, particularly if you use it in conjunction with his >> Controller_Request_Cli class for exposing some of your Zend-based >> application via command line or cron jobs. >> >> I was wondering if this is the recommended approach to exposing global >> resources or is there a Zend Framework "approved" method? > > I've had some back and forth with Cal on his Globals class. :) > > I personally feel that this is primarily the realm of a registry or > dependency injection; a class of static methods is typically difficult > to test against, and makes it more difficult to determine what the > actual dependencies are for classes that pull from it. > > Zend_Application may very well make such a Globals class obsolete, as it > will make it easier to handle your dependencies and push them either > into the registry or directly into the objects that need them. > > -- > Matthew Weier O'Phinney > Software Architect | [hidden email] > Zend Framework | http://framework.zend.com/ |
|
Comments inline: > The better solution is dependency injection, and one facet of this is removing I disagree (if you are talking about DI as a container model) > explicit references to Registry/global items from source code. This is a I agree. > feature the ZF has yet to include but Bradley Holt has ressurected the idea in > the form of Zend_Container ( > http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-+Bradley+Holt ) > and I really really hope it, or an evolution of it (Zend_Application plugin?), I agree > makes it into the library. The last time something like this was proposed it > was bogged down by ever expanding requirements and complexity - True, as DI Containers tend to get. > simpler is better. +1000 here :) .. Now onto the explanation. To sum it up, I do think we need a solution here that will fit the framework, specifically our components- framework wide, as well as application wide. But, DI Containers (to me) have always introduced a level of indirection that is extremely uncommon for PHP. There are two ideas, I would love to see explored, and both of them would be rooted in what I would call a "component development convention", but only at the most forward facing API layer. First, the __construct($options = array()) {} convention for the most forward facing component API constructor adds a level of consistency that makes each and every component look and feel similar in style. This would makes it easy to move from component to component and have a good understanding of what is expected. Zend_Form does this, and to me, it make for the most flexible of components. In each case, $options can be a Zend_Config object, and assoc array of configuration values which can then be pushed down into the varios setXXX() methods, or $options can be the single most well used value, (if this were a Zend_Service component, it could be the API key). I believe this convention is very much used in Solar and its becoming more popular in ZF as we see more components hit the library. Second, I propose we look at creating a Registry class that can be extended (maybe its just a glorified ArrayObject), that can be used and defined by each component. This would allow components to have a *Component Level Registry*. How would this help? Well, instead of there being a dbAdapter key in the Application space registry, Zend_Db_Registry would contain a getDefaultAdapter() method. It might also have a getAdapter($name) method. Also, now the Zend_Db component can manage is component level registry and made the best decisions on how to lazyload and expose its individual pieces to the application layer, or user layer. For Zend_Controller, the api might look like this: Class Zend_Controller_Registry { public function getFront(); public function getDispatcher(); public function getRouter(); public function getRequest(); public function getResponse(); /* .. Others .. */ } Again, this delegate the responsibility down into the actual component of wiring dependencies, and either throwing an exception or lazy-loading when a non-explictily-set wiring is needed. This also alleviates the need for a god-like container to find, or be given a wiring diagram (which could become expensive for instantiation of every PHP object needed) to be able to work within all components and between all components. ALSO (if this is more of a convention Zend_Controller_Registry extends ArrayObject), this would reduce the intra-component dependencies. Also, PHP is not java, so containers need to be rebuilt on each request, whereas in Java the container might need to be built once and put into resident memory for all requests. Those are two ideas I wanted to float ever since hearing of DI. Im sure this will turn in to a blog post, since its more of a how does DI affect PHP sort of question. Let me know what you think. -ralph -- Ralph Schindler Software Engineer | [hidden email] Zend Framework | http://framework.zend.com/ |
|
great post ralph! this is a step into a brighter future ;-D
my two cents though. 1. having __construct(array $options); is cool from a simplicity point but it hides dependencies, since in an array you couldinclude ANYTHING. Most components also don't require the dependencies to be set in constructor, they take all sort of options and set them but don't raise an exception when a depdenency xyz misses. When you have a sensible default depdenency this would make sense though. ezcComponents uses constructor struct objects for configuration. You would have an object Zend_Controller_Config which enforces some variables and is given to the constructor. I like this alot, since objects always allow for more checks on variables than arrays. 2. component registries are a great thing IF they are not singletonized. They allow for type checking and consistency checks where the Zend_Registry does not. You could always make a container go into Zend_Registry to make it global, but this should be optional, and pleeease let us just forget the singleton antipattern. :-) If component registries are used we should make sure that all dependencies are overwritable with them, for example the PluginLoader of Zend_Controller for helpers is hardcoded into the front controller. The component registry should include all objects that might impliciltly be set deep in the object graph of the component. That would be super! 3. Implicit dependencies and work in the Constructor Dependency Injection is also about not having implicit dependencies or much work in the constructor. We should evaluate all components based on this. Having a "new Something()" inside an object constructor is evil, aswell as doing lots of stuff that you cannot influence. greetings, Benjamin On Tuesday 13 January 2009 16:55:57 Ralph Schindler wrote: > Comments inline: > > The better solution is dependency injection, and one facet of this is > > removing > > I disagree (if you are talking about DI as a container model) > > > explicit references to Registry/global items from source code. This is a > > I agree. > > > feature the ZF has yet to include but Bradley Holt has ressurected the > > idea in the form of Zend_Container ( > > http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-+Bradley+Ho > >lt ) and I really really hope it, or an evolution of it (Zend_Application > > plugin?), > > I agree > > > makes it into the library. The last time something like this was proposed > > it was bogged down by ever expanding requirements and complexity - > > True, as DI Containers tend to get. > > > simpler is better. > > +1000 here :) .. Now onto the explanation. > > To sum it up, I do think we need a solution here that will fit the > framework, specifically our components- framework wide, as well as > application wide. > > But, DI Containers (to me) have always introduced a level of indirection > that is extremely uncommon for PHP. There are two ideas, I would love to > see explored, and both of them would be rooted in what I would call a > "component development convention", but only at the most forward facing API > layer. > > First, the __construct($options = array()) {} convention for the most > forward facing component API constructor adds a level of consistency that > makes each and every component look and feel similar in style. This would > makes it easy to move from component to component and have a good > understanding of what is expected. Zend_Form does this, and to me, it make > for the most flexible of components. In each case, $options can be a > Zend_Config object, and assoc array of configuration values which can then > be pushed down into the varios setXXX() methods, or $options can be the > single most well used value, (if this were a Zend_Service component, it > could be the API key). > > I believe this convention is very much used in Solar and its becoming more > popular in ZF as we see more components hit the library. > > Second, I propose we look at creating a Registry class that can be extended > (maybe its just a glorified ArrayObject), that can be used and defined by > each component. This would allow components to have a *Component Level > Registry*. How would this help? Well, instead of there being a dbAdapter > key in the Application space registry, Zend_Db_Registry would contain a > getDefaultAdapter() method. It might also have a getAdapter($name) method. > Also, now the Zend_Db component can manage is component level registry and > made the best decisions on how to lazyload and expose its individual pieces > to the application layer, or user layer. > > For Zend_Controller, the api might look like this: > > > Class Zend_Controller_Registry > { > public function getFront(); > public function getDispatcher(); > public function getRouter(); > public function getRequest(); > public function getResponse(); > /* .. Others .. */ > } > > Again, this delegate the responsibility down into the actual component of > wiring dependencies, and either throwing an exception or lazy-loading when > a non-explictily-set wiring is needed. > > This also alleviates the need for a god-like container to find, or be given > a wiring diagram (which could become expensive for instantiation of every > PHP object needed) to be able to work within all components and between all > components. ALSO (if this is more of a convention Zend_Controller_Registry > extends ArrayObject), this would reduce the intra-component dependencies. > > Also, PHP is not java, so containers need to be rebuilt on each request, > whereas in Java the container might need to be built once and put into > resident memory for all requests. > > Those are two ideas I wanted to float ever since hearing of DI. Im sure > this will turn in to a blog post, since its more of a how does DI affect > PHP sort of question. > > Let me know what you think. > > -ralph -- Benjamin Eberlei http://www.beberlei.de |
|
Administrator
|
-- Benjamin Eberlei <[hidden email]> wrote
(on Tuesday, 13 January 2009, 06:11 PM +0100): > great post ralph! this is a step into a brighter future ;-D > > my two cents though. > > 1. having __construct(array $options); is cool from a simplicity point but it > hides dependencies, since in an array you couldinclude ANYTHING. Most > components also don't require the dependencies to be set in constructor, they > take all sort of options and set them but don't raise an exception when a > depdenency xyz misses. > > When you have a sensible default depdenency this would make sense though. It would be the job of the constructor to ensure that required options are passed, then -- or to provide defaults (potentially through an overloaded accessor that lazyloads a default). > ezcComponents uses constructor struct objects for configuration. You would have > an object Zend_Controller_Config which enforces some variables and is given to > the constructor. I like this alot, since objects always allow for more checks > on variables than arrays. However, this adds another dependency to using the objects -- you then need a value object before you can even use the object. If we allow either an array or a Zend_Config object, we give more flexibility in userland code for how the options are retrieved and passed to the constructors (they could be subtrees of an application-wide configuration object, or a serialized JSON array stored in memcached, etc). > 2. component registries are a great thing IF they are not singletonized. They > allow for type checking and consistency checks where the Zend_Registry does > not. If you look closely at Ralph's example, you'll notice it wasn't static. :) He and I have discussed this a fair bit, and the idea is that factories and other "gateway" classes would push themselves and/or the registry down into the subcomponent objects. > You could always make a container go into Zend_Registry to make it global, but > this should be optional, and pleeease let us just forget the singleton > antipattern. :-) > > If component registries are used we should make sure that all dependencies are > overwritable with them, for example the PluginLoader of Zend_Controller for > helpers is hardcoded into the front controller. The component registry should > include all objects that might impliciltly be set deep in the object graph of > the component. That would be super! > > 3. Implicit dependencies and work in the Constructor > > Dependency Injection is also about not having implicit dependencies or much > work in the constructor. We should evaluate all components based on this. > Having a "new Something()" inside an object constructor is evil, aswell as > doing lots of stuff that you cannot influence. I've been pushing the setOptions/setConfig paradigm for a while, and it works really well with DI. Your constructor then passes the options on to setOptions() which attempts to call setters named after the keys. The result is that it allows you to push your dependencies in at instantiation -- leaving the constructor needing to simply check for required settings (if any) only. > On Tuesday 13 January 2009 16:55:57 Ralph Schindler wrote: > > Comments inline: > > > The better solution is dependency injection, and one facet of this is > > > removing > > > > I disagree (if you are talking about DI as a container model) > > > > > explicit references to Registry/global items from source code. This is a > > > > I agree. > > > > > feature the ZF has yet to include but Bradley Holt has ressurected the > > > idea in the form of Zend_Container ( > > > http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-+Bradley+Ho > > >lt ) and I really really hope it, or an evolution of it (Zend_Application > > > plugin?), > > > > I agree > > > > > makes it into the library. The last time something like this was proposed > > > it was bogged down by ever expanding requirements and complexity - > > > > True, as DI Containers tend to get. > > > > > simpler is better. > > > > +1000 here :) .. Now onto the explanation. > > > > To sum it up, I do think we need a solution here that will fit the > > framework, specifically our components- framework wide, as well as > > application wide. > > > > But, DI Containers (to me) have always introduced a level of indirection > > that is extremely uncommon for PHP. There are two ideas, I would love to > > see explored, and both of them would be rooted in what I would call a > > "component development convention", but only at the most forward facing API > > layer. > > > > First, the __construct($options = array()) {} convention for the most > > forward facing component API constructor adds a level of consistency that > > makes each and every component look and feel similar in style. This would > > makes it easy to move from component to component and have a good > > understanding of what is expected. Zend_Form does this, and to me, it make > > for the most flexible of components. In each case, $options can be a > > Zend_Config object, and assoc array of configuration values which can then > > be pushed down into the varios setXXX() methods, or $options can be the > > single most well used value, (if this were a Zend_Service component, it > > could be the API key). > > > > I believe this convention is very much used in Solar and its becoming more > > popular in ZF as we see more components hit the library. > > > > Second, I propose we look at creating a Registry class that can be extended > > (maybe its just a glorified ArrayObject), that can be used and defined by > > each component. This would allow components to have a *Component Level > > Registry*. How would this help? Well, instead of there being a dbAdapter > > key in the Application space registry, Zend_Db_Registry would contain a > > getDefaultAdapter() method. It might also have a getAdapter($name) method. > > Also, now the Zend_Db component can manage is component level registry and > > made the best decisions on how to lazyload and expose its individual pieces > > to the application layer, or user layer. > > > > For Zend_Controller, the api might look like this: > > > > > > Class Zend_Controller_Registry > > { > > public function getFront(); > > public function getDispatcher(); > > public function getRouter(); > > public function getRequest(); > > public function getResponse(); > > /* .. Others .. */ > > } > > > > Again, this delegate the responsibility down into the actual component of > > wiring dependencies, and either throwing an exception or lazy-loading when > > a non-explictily-set wiring is needed. > > > > This also alleviates the need for a god-like container to find, or be given > > a wiring diagram (which could become expensive for instantiation of every > > PHP object needed) to be able to work within all components and between all > > components. ALSO (if this is more of a convention Zend_Controller_Registry > > extends ArrayObject), this would reduce the intra-component dependencies. > > > > Also, PHP is not java, so containers need to be rebuilt on each request, > > whereas in Java the container might need to be built once and put into > > resident memory for all requests. > > > > Those are two ideas I wanted to float ever since hearing of DI. Im sure > > this will turn in to a blog post, since its more of a how does DI affect > > PHP sort of question. > > > > Let me know what you think. > > > > -ralph > > -- > Benjamin Eberlei > http://www.beberlei.de > -- Matthew Weier O'Phinney Software Architect | [hidden email] Zend Framework | http://framework.zend.com/ |
|
Hello matthew, i would subscribe to such a component based registry thing :-) ok its not up to me, but i think its a very good mixture between DI and simplicity and especially the Controller component would really benefit from the more flexibility. :-) On Tue, 13 Jan 2009 16:38:48 -0500, Matthew Weier O'Phinney <[hidden email]> wrote: > -- Benjamin Eberlei <[hidden email]> wrote > (on Tuesday, 13 January 2009, 06:11 PM +0100): >> great post ralph! this is a step into a brighter future ;-D >> >> my two cents though. >> >> 1. having __construct(array $options); is cool from a simplicity point > but it >> hides dependencies, since in an array you couldinclude ANYTHING. Most >> components also don't require the dependencies to be set in constructor, > they >> take all sort of options and set them but don't raise an exception when > a >> depdenency xyz misses. >> >> When you have a sensible default depdenency this would make sense > though. > > It would be the job of the constructor to ensure that required options > are passed, then -- or to provide defaults (potentially through an > overloaded accessor that lazyloads a default). > >> ezcComponents uses constructor struct objects for configuration. You > would have >> an object Zend_Controller_Config which enforces some variables and is > given to >> the constructor. I like this alot, since objects always allow for more > checks >> on variables than arrays. > > However, this adds another dependency to using the objects -- you then > need a value object before you can even use the object. If we allow > either an array or a Zend_Config object, we give more flexibility in > userland code for how the options are retrieved and passed to the > constructors (they could be subtrees of an application-wide > configuration object, or a serialized JSON array stored in memcached, > etc). > >> 2. component registries are a great thing IF they are not singletonized. > They >> allow for type checking and consistency checks where the Zend_Registry > does >> not. > > If you look closely at Ralph's example, you'll notice it wasn't static. > :) He and I have discussed this a fair bit, and the idea is that > factories and other "gateway" classes would push themselves and/or the > registry down into the subcomponent objects. > >> You could always make a container go into Zend_Registry to make it > global, but >> this should be optional, and pleeease let us just forget the singleton >> antipattern. :-) >> >> If component registries are used we should make sure that all > dependencies are >> overwritable with them, for example the PluginLoader of Zend_Controller > for >> helpers is hardcoded into the front controller. The component registry > should >> include all objects that might impliciltly be set deep in the object > graph of >> the component. That would be super! >> >> 3. Implicit dependencies and work in the Constructor >> >> Dependency Injection is also about not having implicit dependencies or > much >> work in the constructor. We should evaluate all components based on > this. >> Having a "new Something()" inside an object constructor is evil, aswell > as >> doing lots of stuff that you cannot influence. > > I've been pushing the setOptions/setConfig paradigm for a while, and it > works really well with DI. Your constructor then passes the options on > to setOptions() which attempts to call setters named after the keys. The > result is that it allows you to push your dependencies in at > instantiation -- leaving the constructor needing to simply check for > required settings (if any) only. > > >> On Tuesday 13 January 2009 16:55:57 Ralph Schindler wrote: >> > Comments inline: >> > > The better solution is dependency injection, and one facet of this > is >> > > removing >> > >> > I disagree (if you are talking about DI as a container model) >> > >> > > explicit references to Registry/global items from source code. This > is a >> > >> > I agree. >> > >> > > feature the ZF has yet to include but Bradley Holt has ressurected > the >> > > idea in the form of Zend_Container ( >> > > > http://framework.zend.com/wiki/display/ZFPROP/Zend_Container+-+Bradley+Ho >> > >lt ) and I really really hope it, or an evolution of it > (Zend_Application >> > > plugin?), >> > >> > I agree >> > >> > > makes it into the library. The last time something like this was > proposed >> > > it was bogged down by ever expanding requirements and complexity - >> > >> > True, as DI Containers tend to get. >> > >> > > simpler is better. >> > >> > +1000 here :) .. Now onto the explanation. >> > >> > To sum it up, I do think we need a solution here that will fit the >> > framework, specifically our components- framework wide, as well as >> > application wide. >> > >> > But, DI Containers (to me) have always introduced a level of > indirection >> > that is extremely uncommon for PHP. There are two ideas, I would love > to >> > see explored, and both of them would be rooted in what I would call a >> > "component development convention", but only at the most forward > facing API >> > layer. >> > >> > First, the __construct($options = array()) {} convention for the most >> > forward facing component API constructor adds a level of consistency > that >> > makes each and every component look and feel similar in style. This > would >> > makes it easy to move from component to component and have a good >> > understanding of what is expected. Zend_Form does this, and to me, it > make >> > for the most flexible of components. In each case, $options can be a >> > Zend_Config object, and assoc array of configuration values which can > then >> > be pushed down into the varios setXXX() methods, or $options can be > the >> > single most well used value, (if this were a Zend_Service component, > it >> > could be the API key). >> > >> > I believe this convention is very much used in Solar and its becoming > more >> > popular in ZF as we see more components hit the library. >> > >> > Second, I propose we look at creating a Registry class that can be > extended >> > (maybe its just a glorified ArrayObject), that can be used and defined > by >> > each component. This would allow components to have a *Component > Level >> > Registry*. How would this help? Well, instead of there being a > dbAdapter >> > key in the Application space registry, Zend_Db_Registry would contain > a >> > getDefaultAdapter() method. It might also have a getAdapter($name) > method. >> > Also, now the Zend_Db component can manage is component level registry > and >> > made the best decisions on how to lazyload and expose its individual > pieces >> > to the application layer, or user layer. >> > >> > For Zend_Controller, the api might look like this: >> > >> > >> > Class Zend_Controller_Registry >> > { >> > public function getFront(); >> > public function getDispatcher(); >> > public function getRouter(); >> > public function getRequest(); >> > public function getResponse(); >> > /* .. Others .. */ >> > } >> > >> > Again, this delegate the responsibility down into the actual component > of >> > wiring dependencies, and either throwing an exception or lazy-loading > when >> > a non-explictily-set wiring is needed. >> > >> > This also alleviates the need for a god-like container to find, or be > given >> > a wiring diagram (which could become expensive for instantiation of > every >> > PHP object needed) to be able to work within all components and > between all >> > components. ALSO (if this is more of a convention > Zend_Controller_Registry >> > extends ArrayObject), this would reduce the intra-component > dependencies. >> > >> > Also, PHP is not java, so containers need to be rebuilt on each > request, >> > whereas in Java the container might need to be built once and put into >> > resident memory for all requests. >> > >> > Those are two ideas I wanted to float ever since hearing of DI. Im > sure >> > this will turn in to a blog post, since its more of a how does DI > affect >> > PHP sort of question. >> > >> > Let me know what you think. >> > >> > -ralph >> >> -- >> Benjamin Eberlei >> http://www.beberlei.de >> > > -- > Matthew Weier O'Phinney > Software Architect | [hidden email] > Zend Framework | http://framework.zend.com/ |
|
In reply to this post by Ralph Schindler-2
On Jan 13, 2009, at 10:55 , Ralph Schindler wrote:
> First, the __construct($options = array()) {} convention for the most > forward facing component API constructor adds a level of consistency > that > makes each and every component look and feel similar in style. This > would > makes it easy to move from component to component and have a good > understanding of what is expected. ... > I believe this convention is very much used in Solar and its > becoming more > popular in ZF as we see more components hit the library. Correct. In Solar, it is used always and only; that is, every constructor for every class is identical: public function __construct($config = null) { ... } It helps to standardize configuration of instances and aids greatly in Dependency Injection and Service Location. I call this a "universal constructor." I don't know if this pattern has been cataloged anywhere. You can read a bit more about it on these pages: - <http://paul-m-jones.com/?p=197> (old but mostly valid) - <http://solarphp.org/manual:project_standards:constructor_parameters> -- Paul M. Jones http://paul-m-jones.com/ |
|
In reply to this post by weierophinney
I've been pushing the setOptions/setConfig paradigm for a while, and it works really well with DI. Your constructor then passes the options on to setOptions() which attempts to call setters named after the keys. The result is that it allows you to push your dependencies in at instantiation -- leaving the constructor needing to simply check for required settings (if any) only. The primary problem with configuration in Zend Framework is that configuration is left up to each class and so each class handles it differently. The primary problem with PHP is lack of mixins, which would elegantly solve the first problem. I solved the issue at my job like this:
<?php class My_Config extends Zend_Config { /** * Configures an object. * * @param object|string $caller Calling object or class name (for static usage)
* @param Zend_Config|array $config Configuration * @param string $section Config section, if using Zend_Config * @return array Array representation of options, after taking section into
* account if applicable */ public static function setConfig($caller, $config, $section = null) { $config = self::getOptionsFromConfig($config, $section);
foreach ($config as $option => $value) { $method = 'set' . ucfirst($option); if (method_exists($caller, $method)) {
if ($value instanceof Zend_Config) { $value = $value->toArray(); } if (is_object($caller)) { $caller->$method($value);
} else { call_user_func(array($caller, $method), $value); } } } return $config;
} /** * Returns an options array from a config. * * @param object|string $caller Calling object or class name (for static usage)
* @param Zend_Config|array $config Configuration * @param string $section Config section, if using Zend_Config * @return array Array representation of options, after taking section into
* account if applicable */ public static function getOptionsFromConfig($config, $section = null) { if ($config instanceof self) {
if ($section !== null) { if ($config->{$section} === null) { throw new My_Config_Exception("No configuration in section '{$section}'");
} $config = $config->{$section}; } $config = $config->toArray(); } return $config; } } Each configurable class then has its own setConfig() method that proxies to My_Config::setConfig() and perhaps sets up its constructor in the following way, if it makes sense:
/** * Constructor. * * @param Zend_Config|array $config (Optional) Configuration * @param string $section (Optional) Config section */
public function __construct($config = null, $section = null) { if ($config !== null) { $this->setConfig($config, $section); } }
/** * Sets parameters from a Zend_Config instance. * * @param Zend_Config|array $config (Optional) Configuration * @param string $section (Optional) Config section * @return My_Class $this
*/ public function setConfig($config, $section = null) { My_Config::setConfig($this, $config, $section); return $this; }
This is a pretty flexible approach that allows instance or static method calls on the caller and does not distinguish between Zend_Config objects and arrays. In my experience there is no practical benefit, and some drawbacks, to distinguishing between separate setConfig() and setOptions() methods. If you must store the configuration in the object, for example to allow a generic getOption($optionName) method, the logic is still off-loaded to the other class.
Anyway, that's my two cents. -Matt On Tue, Jan 13, 2009 at 1:38 PM, Matthew Weier O'Phinney <[hidden email]> wrote: -- Benjamin Eberlei <[hidden email]> wrote |
|
> The primary problem with configuration in Zend Framework is that configuration > is left up to each class and so each class handles it differently. The > primary problem with PHP is lack of mixins, which would elegantly solve the > first problem. I solved the issue at my job like this: The initial problem is that each class solves the configuration without any regard to how other components solve the configuration issue (the common convention). This thus creates a somewhat ambiguous API. I think one of the stories I'd like to see told with ZF2 is more one of API consistency, fewer statics, and those statics that are used are part of an acceptable list of static usages... All for the purpose of keeping things consistent. > public static function setConfig($caller, $config, $section = null) > { > $config = self::getOptionsFromConfig($config, $section); > > foreach ($config as $option => $value) { > $method = 'set' . ucfirst($option); > > if (method_exists($caller, $method)) { > if ($value instanceof Zend_Config) { > $value = $value->toArray(); > } > > if (is_object($caller)) { > $caller->$method($value); > } else { > call_user_func(array($caller, $method), $value); > } > } > } > > return $config; > } This is more or less how you see the setOptions() implementation in a few classes currently. The idea that you proxy the key to a mutator (setter) setSomething($value) method. > This is a pretty flexible approach that allows instance or static method calls > on the caller and does not distinguish between Zend_Config objects and arrays. > In my experience there is no practical benefit, and some drawbacks, to > distinguishing between separate setConfig() and setOptions() methods. If you > must store the configuration in the object, for example to allow a generic > getOption($optionName) method, the logic is still off-loaded to the other > class. > The major difference I think Matthew W.O. Was trying to demonstrate is that those methods have different signatures: Public function setConfig(Zend_Config $config); Public function setOptions(Array $options); This creates a very loose coupling on Zend_Config for instances where one would like to use Zend_Config. In other cases, an associative array is just fine. -ralph -- Ralph Schindler Software Engineer | [hidden email] Zend Framework | http://framework.zend.com/ |
This is more or less how you see the setOptions() implementation in a few classes currently. The idea that you proxy the key to a mutator (setter) setSomething($value) method. Yep, except it's in one place instead of several with tiny variations. And it works with static setters. -Matt
On Fri, Jan 16, 2009 at 9:12 AM, Ralph Schindler <[hidden email]> wrote:
|
|
Administrator
|
-- Matthew Ratzloff <[hidden email]> wrote
(on Friday, 16 January 2009, 09:42 AM -0800): > This is more or less how you see the setOptions() implementation in a few > > classes currently. The idea that you proxy the key to a mutator (setter) > > setSomething($value) method. > > > Yep, except it's in one place instead of several with tiny variations. And it > works with static setters. But your solution would introduce a hard dependency on another class. One principle we have espoused from the beginning is that we would not have a "base" class used by all other classes, in an effort to reduce dependencies. There's also good reasons to implement setOptions functionality on a class-by-class basis. One is to allow for class-specific behavior; for instance, one class might discard unknown keys, another might set them as class metadata. Another implementation might proxy to protected methods. Another implementation might make setOptions protected so that the class can be used within a service. In other words, the variations in _behavior_ have their purpose. But the constructors would still have the same public API -- leaving a consistent _use case_ that can be messaged. > On Fri, Jan 16, 2009 at 9:12 AM, Ralph Schindler <[hidden email]> > wrote: > > > > The primary problem with configuration in Zend Framework is that > configuration > > is left up to each class and so each class handles it differently. The > > primary problem with PHP is lack of mixins, which would elegantly solve > the > > first problem. I solved the issue at my job like this: > > > The initial problem is that each class solves the configuration without any > regard to how other components solve the configuration issue (the common > convention). This thus creates a somewhat ambiguous API. I think one of > the stories I'd like to see told with ZF2 is more one of API consistency, > fewer statics, and those statics that are used are part of an acceptable > list of static usages... All for the purpose of keeping things consistent. > > > > public static function setConfig($caller, $config, $section = null) > > { > > $config = self::getOptionsFromConfig($config, $section); > > > > foreach ($config as $option => $value) { > > $method = 'set' . ucfirst($option); > > > > if (method_exists($caller, $method)) { > > if ($value instanceof Zend_Config) { > > $value = $value->toArray(); > > } > > > > if (is_object($caller)) { > > $caller->$method($value); > > } else { > > call_user_func(array($caller, $method), $value); > > } > > } > > } > > > > return $config; > > } > > > This is more or less how you see the setOptions() implementation in a few > classes currently. The idea that you proxy the key to a mutator (setter) > setSomething($value) method. > > > > This is a pretty flexible approach that allows instance or static method > calls > > on the caller and does not distinguish between Zend_Config objects and > arrays. > > In my experience there is no practical benefit, and some drawbacks, to > > distinguishing between separate setConfig() and setOptions() methods. If > you > > must store the configuration in the object, for example to allow a > generic > > getOption($optionName) method, the logic is still off-loaded to the other > > class. > > > > > The major difference I think Matthew W.O. Was trying to demonstrate is that > those methods have different signatures: > > Public function setConfig(Zend_Config $config); > Public function setOptions(Array $options); > > This creates a very loose coupling on Zend_Config for instances where one > would like to use Zend_Config. In other cases, an associative array is just > fine. > > -ralph > > -- > Ralph Schindler > Software Engineer | [hidden email] > Zend Framework | http://framework.zend.com/ > > -- Matthew Weier O'Phinney Software Architect | [hidden email] Zend Framework | http://framework.zend.com/ |
|
Since configuration is such a fundamental aspect of most components, perhaps this can be added to the coding standards, along with whatever suggested implementations the community agrees upon?
-Matt
On Sat, Jan 17, 2009 at 6:55 AM, Matthew Weier O'Phinney <[hidden email]> wrote: -- Matthew Ratzloff <[hidden email]> wrote |
|
Administrator
|
-- Matthew Ratzloff <[hidden email]> wrote
(on Saturday, 17 January 2009, 12:14 PM -0800): > Since configuration is such a fundamental aspect of most components, perhaps > this can be added to the coding standards, along with whatever suggested > implementations the community agrees upon? I've actually been planning on reviewing the CS and proposing changes such as this for some months now; just waiting for it to move up in priority in my backlog. > On Sat, Jan 17, 2009 at 6:55 AM, Matthew Weier O'Phinney <[hidden email]> > wrote: > > -- Matthew Ratzloff <[hidden email]> wrote > (on Friday, 16 January 2009, 09:42 AM -0800): > > This is more or less how you see the setOptions() implementation in a > few > > > > classes currently. The idea that you proxy the key to a mutator > (setter) > > > > setSomething($value) method. > > > > > > Yep, except it's in one place instead of several with tiny variations. > And it > > works with static setters. > > But your solution would introduce a hard dependency on another class. > One principle we have espoused from the beginning is that we would not > have a "base" class used by all other classes, in an effort to reduce > dependencies. > > There's also good reasons to implement setOptions functionality on a > class-by-class basis. One is to allow for class-specific behavior; for > instance, one class might discard unknown keys, another might set them > as class metadata. Another implementation might proxy to protected > methods. Another implementation might make setOptions protected so that > the class can be used within a service. > > In other words, the variations in _behavior_ have their purpose. But the > constructors would still have the same public API -- leaving a > consistent _use case_ that can be messaged. > > > > On Fri, Jan 16, 2009 at 9:12 AM, Ralph Schindler < > [hidden email]> > > wrote: > > > > > > > The primary problem with configuration in Zend Framework is that > > configuration > > > is left up to each class and so each class handles it differently. > The > > > primary problem with PHP is lack of mixins, which would elegantly > solve > > the > > > first problem. I solved the issue at my job like this: > > > > > > The initial problem is that each class solves the configuration > without any > > regard to how other components solve the configuration issue (the > common > > convention). This thus creates a somewhat ambiguous API. I think > one of > > the stories I'd like to see told with ZF2 is more one of API > consistency, > > fewer statics, and those statics that are used are part of an > acceptable > > list of static usages... All for the purpose of keeping things > consistent. > > > > > > > public static function setConfig($caller, $config, $section = > null) > > > { > > > $config = self::getOptionsFromConfig($config, $section); > > > > > > foreach ($config as $option => $value) { > > > $method = 'set' . ucfirst($option); > > > > > > if (method_exists($caller, $method)) { > > > if ($value instanceof Zend_Config) { > > > $value = $value->toArray(); > > > } > > > > > > if (is_object($caller)) { > > > $caller->$method($value); > > > } else { > > > call_user_func(array($caller, $method), > $value); > > > } > > > } > > > } > > > > > > return $config; > > > } > > > > > > This is more or less how you see the setOptions() implementation in a > few > > classes currently. The idea that you proxy the key to a mutator > (setter) > > setSomething($value) method. > > > > > > > This is a pretty flexible approach that allows instance or static > method > > calls > > > on the caller and does not distinguish between Zend_Config objects > and > > arrays. > > > In my experience there is no practical benefit, and some drawbacks, > to > > > distinguishing between separate setConfig() and setOptions() > methods. If > > you > > > must store the configuration in the object, for example to allow a > > generic > > > getOption($optionName) method, the logic is still off-loaded to the > other > > > class. > > > > > > > > > The major difference I think Matthew W.O. Was trying to demonstrate > is that > > those methods have different signatures: > > > > Public function setConfig(Zend_Config $config); > > Public function setOptions(Array $options); > > > > This creates a very loose coupling on Zend_Config for instances where > one > > would like to use Zend_Config. In other cases, an associative array > is just > > fine. > > > > -ralph > > > > -- > > Ralph Schindler > > Software Engineer | [hidden email] > > Zend Framework | http://framework.zend.com/ > > > > > > -- > Matthew Weier O'Phinney > Software Architect | [hidden email] > Zend Framework | http://framework.zend.com/ > > -- Matthew Weier O'Phinney Software Architect | [hidden email] Zend Framework | http://framework.zend.com/ |
| Powered by Nabble | Edit this page |
