Greetings, I'm being tasked to come up with a way to handle runtime configuration for our Symfony2 application, in a manner quite similar to what GitHub and Flickr do with feature flags/flippers:
- https://github.com/blog/677-how-we-deploy-new-features - http://code.flickr.com/blog/2009/12/02/flipping-out/ Practically, this means we'll have a CRUD module in the admin that allows certain app options to be configured on the fly. These options are stored in a key/value datastore (e.g. Redis, a SQL table, etc). The options themselves might correspond to an integer value for some limit, or a boolean to enable/disable some functionality in a service. Anything, really. My first thought was to implement a loader class (as we have for INI, Yaml, XML, etc.), but I would run into a roadblock when working with a dumped container in a production environment. PhpDumper dumps all dereferenced parameters into a getDefaultParameters() method, which is used in the constructor to initialize the parameters array. If I imported from a datastore in the same manner as existing loaders, my dynamic values would be frozen in a dumped container. The only way to achieve runtime-configurability with such an implementation would entail triggering a container recompile or cache clear on all of my web nodes after a config parameter is changed in our CRUD. This is messy and not ideal at all. The logical improvement to the dumped container would be to merge in an array from a new method (i.e. getRuntimeParameters()) atop the frozen, default parameters. That's a significant architectural change. Before considering it, I investigated a few other implementations: The most practical solution is to have a service, let's call it RuntimeConfiguration, responsible for fetching these parameters from our key/value datastore. This service might implement the FrozenParameterBag interface (changing would only be done by separate admin forms) and be injected into any other service that depends on a runtime option. For instance, my depending service's constructor might look like: public __construct(RuntimeConfiguration $rc) { $this->enabled = $rc['my.service.enabled']; // or $this->limit = $rc->get('my.service.limit'); } This works, of course, but it's not ideal in the same sense that injecting a service container is not ideal. My service really only depends on two specific parameters, not a RuntimeConfiguration instance. Furthermore, it involves hard-coding the parameter names in my PHP class, while all other parameter references are defined in my Yaml/XML configuration. An alternative solution would be inject these parameters using factory-method support of the service configuration: <service id="my.service" class="%my.service.class%"> <argument type="service"> <service class="RequiredButIgnored" factory-service="runtime.configuration" factory-method="get" public="false"> <argument>my.service.enabled</argument> </service> </argument> </service> *Note: I think the class attribute is required, even though it isn't used for factory-generated services.* This keeps the parameter references in my service configuration; my PHP source is more straight-forward and testable. This comes at the cost of a more-verbose service configuration and obvious abuse of the factory-service feature. So let's jump back to my original idea of baking this functionality into the container. It would have to be compatible with dumped containers, but built-in support might allow for much friendlier syntax: <parameters> <parameter key="my.service.enabled" runtime="true" /> </parameters> Kris Wallsmith suggested using a different attribute: <parameters> <parameter key="my.service.enabled" freeze="false" /> </parameters> In both cases, I left the value empty (null), since we expect it to come from some runtime configuration provider. I suppose we could define a default value if we wish. Most importantly, though, the parameter is explicitly defined, which means the compiler will continue to catch references to undefined parameters. For my immediate needs, I'll likely use my second proposal (factory-service abuse) as it doesn't make sense to hack up the DI component in our fork. In the meantime, I'd welcome any feedback on implementing native support for this. If there is enough support, perhaps it would be a reasonable addition for Symfony 2.1. -- jeremy mikola -- If you want to report a vulnerability issue on symfony, please send it to security at symfony-project.com You received this message because you are subscribed to the Google Groups "symfony developers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/symfony-devs?hl=en
