Pheat
PHP 5.4+ Feature Manager
, (*1)
Pheat is a simple implementation of a feature manager for PHP 5.4+. The main abstractions it uses are:, (*2)
- A
Provider
knows about the status of a list of Feature
instances
- A
Provider
can vary its list of features based on the Context
- A
Manager
can tell you if a specific feature is active or inactive, based on a list of Provider
instances, by merging their statuses
Installation
Pheat uses PSR4 for autoloading. It's available as vend/pheat
on Packagist., (*3)
Usage
Checking for Features
The main instance you'll use for feature management is the Pheat\Manager
. At its simplest, the manager tells you whether a feature should be treated as active or inactive:, (*4)
if ($manager->resolve('fancy_graphics')) {
// Output the fancy graphics
}
Boolean Semantics of Status
The resolve
method always returns a boolean or null:
* true
if the feature should be considered active
* false
if the feature should be considered inactive
* null
if nothing is known about the state of the feature, (*5)
Most of the time, if nothing is known about a feature, you don't want to enable it. So, you can just do a loose "falsish" check on the return value from resolve
., (*6)
If you call the manager's resolveFeature
method you'll receive a FeatureInterface
instance (rather than a status value). This can be helpful if you need to know why a feature is active, because it can tell you which provider is marking it as active., (*7)
This is also where you'd implement variants, ratios or buckets: more complex ways of assigning features. The FeatureInterface
ensures the information about why a feature is active or inactive can be interrogated in more detail., (*8)
Configuration
Manager
When you create a Manager
, you'll usually give it a Context
and a list of Providers
., (*9)
Context
The Context
is a collection of circumstances under which the feature manager is running. The information in the context is what the manager uses to decide whether a feature should be enabled. So, for example, you might have your end-users' usernames in the context: that way, you'd be able to manage features for specific users (or across the pool of users as a percentage)., (*10)
The Context
is passed to the Manager
. Once the Manager
resolves the current feature list once (and caches it), the manager is locked, and changes to the context won't affect the features. Create a new Manager
to refresh the features., (*11)
You can use the default Context
implementation (which is a simple array-backed attribute bag), or your own implementation of ContextInterface
., (*12)
Providers
Providers tell the manager which features exist, and when they should be enabled. Providers must implement Pheat\ProviderInterface
. Providers should be instantiated and added to the manager either when you create it:, (*13)
$manager = new Pheat\Manager($context, [
new My\Custom\Provider(),
new My\Other\Provider()
]);
Or after it's created:, (*14)
$manager->addProvider(new My\Custom\Provider())
Providers are kept and processed in an ordered list (so the order in which you call addProvider
does matter)., (*15)
Feature
The Feature
object holds the status for a named feature. It also holds a reference to the provider which gave the information about the feature., (*16)
::resolve()
When two or more providers give information about the same feature (according to the feature name), their statuses are merged to find the provider which should 'win' (and control the final value of whether the feature is enabled.) This is done by passing the previously controlling feature into the 'new' feature to be merged's resolve()
method., (*17)
The resolve()
method returns the Feature
instance that should now be considered in control. This makes it a good place to implement complex logic, like enabling a feature for a ratio or sample of users., (*18)
Default Resolution
When two features are resolved together, in the default implementation, the Feature
that will end up controlling the status is shown in bold., (*19)
Previous Status |
New Status |
Active |
Active |
Active |
Inactive |
Active |
Unknown |
Inactive |
Active |
Inactive |
Inactive |
Inactive |
Unknown |
Unknown |
Active |
Unknown |
Inactive |
Unknown |
Unknown |