Allows you to flag features as ready/not ready and live/not live from a central "switchboard". Based on these switches your codebase can act one way or another.
-
Because this is an unpublished package, you must define it's repository in your project's composer.json file. Add the following to composer.json:
"repositories": [ { "type": "github", "url": "https://github.com/aklump/drupal_feature_switches" } ]
-
Then
composer require aklump_drupal/feature_switches:^0.0
-
It will be installed to web/modules/custom/feature_switches, which should be excluded from source control.
-
Enable this module.
-
See section below about declaring as a Drupal dependency.
-
Create a file in the same directory as settings.php. Call it feature_switches.php.
-
Add to settings.php, the following. Note: you should add this AFTER
$config['system.logging']['error_level']
otherwise you may not see the expected error output, if your features are in error.include_once __DIR__ . '/feature_switches.php';
-
Open feature_switches.php and add one or more features, like this:
\Drupal\feature_switches\FeatureSwitches::getOperator()
->add(\Drupal\feature_switches\Feature::create('show_outlines')
->setDescription('Add outlines to all images.')
->setIsReady(TRUE)
->turnOn()
)
->add(\Drupal\feature_switches\Feature::create('user_files_download')
->setDescription('Allow users to download their own backups.')
->setIsReady(TRUE)
->turnOff()
);
For dynamic switch values--such as those depending on the DI container--you will need to set those switches later in the bootstrap of Drupal, for example inside an event listener.
To require that any live feature must also be marked as ready, set the \Drupal\feature_switches\OperatorOptions::REQUIRE_READY_LIVE
option. Doing so will cause a \Drupal\feature_switches\FeatureNotReadyException
to be thrown if you try to add a feature that is live but not ready.
FeatureSwitches::setOptions(\Drupal\feature_switches\OperatorOptions::REQUIRE_READY_LIVE);
This has to be done before adding features, otherwise no exceptions are thrown.
If you have a switch that is dependent on the current user having, say, a given role, you will need to wait until that current user is loaded to calculate that value and set the switch since the container is not yet initialized in settings.php when you defined the switch. So to do that, you can listen for the \Symfony\Component\HttpKernel\KernelEvents::REQUEST
event, and then set the value accordingly.
It's possible to set a switch anywhere in your code, so this is just a tested suggestion. This event is the earliest point when the user is available, in the Drupal bootstrap.
When you do this you must have a custom module, where you can add the event listener.
And you must declare a dependency on the feature_switches module.
my_module/src/EventSubscriber/MyModuleFeatureSwitches.php
namespace Drupal\my_module\EventSubscriber;
use Drupal\feature_switches\FeatureSwitches;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MyModuleFeatureSwitches implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
// Best practice; use class_exists().
// @link https://www.drupal.org/project/drupal/issues/2825358
if (class_exists(KernelEvents::CLASS)) {
$events[KernelEvents::REQUEST][] = ['setUserDependentFeatureSwitches', 0];
}
return $events;
}
/**
* Respond to a new request event.
*
* @param RequestEvent $event
* A new event instance.
*/
public function setUserDependentFeatureSwitches(RequestEvent $event) {
$early_access = in_array('early_access', \Drupal::currentUser()
->getRoles(TRUE));
FeatureSwitches::get('user_files_download')->setIsLive($early_access);
}
}
FeatureSwitches::get('bogus')->turnOn()
will fail quietly, whenbogus
is not added. In other wordssetIsLive()
will have no effect. If you callFeatureSwitches::isLive('bogus)
it will returnFALSE
.
my_module.services.yml
services:
my_module.feature_switches:
class: \Drupal\my_module\EventSubscriber\MyModuleFeatureSwitches
tags: [ { name: event_subscriber } ]
my_module.info.yml
dependencies:
- feature_switches:feature_switches
The whole point of this module to is allow your codebase to react differently based on a feature being live or not. Once your features have been created, it's quite simple to check them.
if (\Drupal\feature_switches\FeatureSwitches::isLive('user_files_download')) {
// Proceed with the process...
}
/** @var \Drupal\feature_switches\Feature $foo_feature */
$download_feature = \Drupal\feature_switches\FeatureSwitches::get('download');
$download_feature->getId();
$download_feature->getDescription();
// Note: these two are synonymous.
$download_feature->isReady();
$download_feature->isLive();