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

Reply via email to