Developed with love by KnpLabs Hire us for your project!
32

features-bundle

by yannickl88

Symfony bundle for managing feature tags

features-bundle

This Symfony bundle provides a way of managing features within a project. A common use-case is to have a certain feature only active under certain condition. Examples would be that you want to activate a feature when the use has a certain role, or when you are not in a production environment (think of testing).

With this bundle you can configure features to be active or inactive. Using resolvers you decide when a feature is active or not.

Requirements:
- PHP 5.5 or higher, including php 7
- Symfony 2.7 or higher, including 3.0

Recommended installation is via composer: composer require yannickl88/features-bundle.

After that, you need to register the bundle in the kernel of your application:

<?php
// app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            new Yannickl88\FeaturesBundle\FeaturesBundle(),
            // …
        ];
    }
}

Usage

All configuration is done using services and your application config. For the following example we want to enable a feature when the GET parameter beta is set to on.

So configuring your feature in the config.yml of your application.

features:
    tags:
        beta: # our feature tag
            request: ["beta", "on"] # 'app.features.request_resolver' will resolve this key

Here we define a feature tag beta which will be resolved with the request resolver. Now we need to configure the request resolver. We do this with the following service definition:
yml
services:
app.features.request_resolver:
class: App\Feature\RequestResolver
arguments:
- "@request_stack"
tags:
# config-key is set to resolve the configured key: "request" with the options "beta" and "on"
- { name: features.resolver, config-key: request }

Here we create the app.features.request_resolver service and tag it with features.resolver. This will then be picked up by the bundle and be registered so we can use it in our feature tags. What we also provide is a config-key value. This is the key that we defined in the config.yml under the beta tag. This will glue your config to your resolver.

Final thing to do is implement the RequestResolver:
```php
namespace App\Feature;

use Symfony\Component\HttpFoundation\RequestStack;
use Yannickl88\FeaturesBundle\Feature\FeatureResolverInterface;

class RequestResolver implements FeatureResolverInterface
{
private $request_stack;

public function __construct(RequestStack $request_stack)
{
    $this->request_stack = $request_stack;
}

/**
 * {@inheritdoc}
 */
public function isActive(array $options = [])
{
    // Feature is inactive when there is no request
    if (null === $request = $this->request_stack->getMasterRequest()) {
        return false;
    }

    // $options contains ["beta", "on"] for the 'beta' feature tag
    list($key, $expected_value) = $options;

    return $request->get($key) === $expected_value;
}

}

Now we can start using the feature in our code. So if I want to check for a feature I can inject it as follows:
yml
services:
app.some.service:
class: App\Some\Service
arguments:
- "@features.tag"
tags:
- { name: features.tag, tag: beta }

Notice here that we do not inject the feature directly, but tag the service. The bundle will replace the feature for you. So you can use it as follows in your code:
php
namespace App\Some;

use Yannickl88\FeaturesBundle\Feature\Feature;

class Service
{
private $feature;

public function __construct(Feature $feature)
{
    $this->feature = $feature;
}

public function someMethod()
{
    if ($this->feature->isActive()) {
        // do some extra beta logic when this feature is active
    }
}

}
``
So if I now add
?beta=on` to my URL. The feature will trigger.

Note: If you remove the tag, it will inject a deprecated feature. This deprecated feature will trigger a warning when the isActive is used so you will quickly see where unused feature are used.

Twig

If it also possible to check a feature in your twig templates. Simply use the feature function to check if a feature is enabled.

{% if feature("beta") %}
    {# do some extra beta logic when this feature is active #}
{% endif %}

Advanced Topics

It is possible to configure multiple resolvers per feature tag. You can simply keep adding more in the config.yml. So in the example we can extend it to:
yml
features:
tags:
beta:
request: ["beta", "on"]
other: ~
more: ["foo"]

All resolvers must now resolve to true in order for this feature to be active. This is usefull if you want to check for multiple conditions.

Furthermore, if you want to have multiple resolvers where only one needs to resolve to true, you can use the chain resolver. This can be done as follows:
yml
features:
tags:
beta:
chain:
request: ["beta", "on"]
other: ~
more: ["foo"]

Notice here we have as resolver chain and under this we have your config as before.

The MIT License (MIT)

Copyright (c) 2016 Yannick de Lange

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
  • Added better autowiring support (#11)
    By web-flow, 4 months ago
  • Remove redundant brace (#10)
    By yannickl88, 5 months ago
  • Added autowire support (#8)
    By yannickl88, 6 months ago
  • Made the bundle compatible with SF4 (#7)
    By yannickl88, 6 months ago
  • Merge pull request #6 from greg0ire/make_composer_outdated_empty
    By web-flow, 1 year ago
  • removed the 3.0 from travis and require phpunit 5
    By yannickl88, 1 year ago
  • Allow Twig 2
    By greg0ire, 1 year ago
  • Migrate to phpunit 6
    By greg0ire, 1 year ago
  • Merge pull request #4 from greg0ire/document_how_to_register_the_bundle
    By web-flow, 1 year ago
  • Document how to register the bundle
    By greg0ire, 1 year ago
  • Fixed issue with feature tag names not being using in the same syntax
    By yannickl88, 1 year ago
  • Updated README.md to include the chain resolver.
    By yannickl88, 2 years ago
  • Fixed typo in unit test :D
    By yannickl88, 2 years ago
  • Fixed typo
    By yannickl88, 2 years ago
  • Added support for a chain resolver
    By yannickl88, 2 years ago
  • Made feature container lazy load features
    By yannickl88, 2 years ago
  • Updated readme
    By yannickl88, 2 years ago
  • Added twig support for checking features
    By yannickl88, 2 years ago
  • Update README.md
    By yannickl88, 2 years ago
  • Update README.md
    By yannickl88, 2 years ago
  • Update README.md
    By yannickl88, 2 years ago
  • Update README.md
    By yannickl88, 2 years ago
  • Update README.md
    By yannickl88, 2 years ago
  • Added missing @param
    By yannickl88, 2 years ago
  • Cleanup
    By yannickl88, 2 years ago
  • Added unit tests
    By yannickl88, 2 years ago
  • Made feature resolve at run-time
    By yannickl88, 2 years ago
  • Cleaned up extension a bit
    By yannickl88, 2 years ago
  • Added missing @author tags
    By yannickl88, 2 years ago
  • Extended test to work with two resolvers
    By yannickl88, 2 years ago