Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/vendor
/.idea
/build

.phpunit.result.cache
phpunit.xml
Expand Down
203 changes: 163 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,84 +23,96 @@ Documentation for configuration can be found in [config/unleash.php](https://git

## Usage

```php
use \MikeFrancis\LaravelUnleash\Unleash;

$unleash = app(Unleash::class);
This package provides a `Unleash` facade for quick and easy usage. Alternatively, you can use the aliased `Feature` facade instead.

if ($unleash->isFeatureEnabled('myAwesomeFeature')) {
```php
if (\Unleash::enabled('myAwesomeFeature')) {
// Congratulations, you can see this awesome feature!
}

if ($unleash->isFeatureDisabled('myAwesomeFeature')) {
if (\Unleash::disabled('myAwesomeFeature')) {
// Check back later for more features!
}

$feature = $unleash->getFeature('myAwesomeFeature');
$feature = \Unleash::get('myAwesomeFeature');

$allFeatures = $unleash->getFeatures();
$allFeatures = \Unleash::all();
```

### Facades
### Unleash Client Consistency

You can use the `Unleash` facade:
Both the `Unleash` and `Feature` facade support methods consistent with standard Unleash clients:

```php
use Unleash;

if (Unleash::isFeatureEnabled('myAwesomeFeature')) {
if (\Unleash::isFeatureEnabled('myAwesomeFeature')) {
// Congratulations, you can see this awesome feature!
}

if (Unleash::isFeatureDisabled('myAwesomeFeature')) {
if (\Unleash::isFeatureDisabled('myAwesomeFeature')) {
// Check back later for more features!
}

$feature = Unleash::getFeature('myAwesomeFeature');
$feature = \Unleash::getFeature('myAwesomeFeature');

$allFeatures = Unleash::getFeatures();
$allFeatures = \Unleash::getFeatures();
```

or use the generically named `Feature` facade:
## Strategies

```php
use Feature;
To enable or disable strategies, add or remove from `unleash.strategies` config in your `unleash.php` config file.

if (Feature::enabled('myAwesomeFeature')) {
// Congratulations, you can see this awesome feature!
}
### Custom Strategies

if (Feature::disabled('myAwesomeFeature')) {
// Check back later for more features!
}
Custom strategies must implement `\MikeFrancis\LaravelUnleash\Strategies\Contracts\Strategy` or if your strategy relies on dynamic data at runtime it should implement `\MikeFrancis\LaravelUnleash\Strategies\Contracts\DynamicStrategy`.

$feature = Feature::get('myAwesomeFeature');
```php
use \MikeFrancis\LaravelUnleash\Strategies\Contracts\Strategy;
use \Illuminate\Http\Request;

$allFeatures = Feature::all();
class CustomStrategy implements Strategy {
public function isEnabled(array $params, Request $request) : bool {
// logic here
return true || false;
}
}
```

### Dynamic Arguments
### Dynamic Strategies

If your strategy relies on dynamic data at runtime, you can pass additional arguments to the feature check functions:
When implementing `DynamicStrategy` you can pass additional arguments to the feature check functions which will be passed as extra arguments to the `isEnabled()` method:

```php
use \MikeFrancis\LaravelUnleash\Unleash;
use Config;

$unleash = app(Unleash::class);

$allowList = config('app.allow_list');

if ($unleash->isFeatureEnabled('myAwesomeFeature', $allowList)) {
if (Unleash::enabled('myAwesomeFeature', $allowList)) {
// Congratulations, you can see this awesome feature!
}

if ($unleash->isFeatureDisabled('myAwesomeFeature', $allowList)) {
if (Unleash::disabled('myAwesomeFeature', $allowList)) {
// Check back later for more features!
}
```

### Blade
## Variants

To use variant support, define your variants on the feature and use:

```php
$color = \Unleash::variant('title-color', '#000')->payload->value;
```

This will return the correct variant for the user, or the default if the feature flag is disabled or no valid variant is found.

The variant payload will be one of the following, depending on the variant type:

- `\MikeFrancis\LaravelUnleash\Values\Variant\PayloadCSV`
- `\MikeFrancis\LaravelUnleash\Values\Variant\PayloadJSON`
- `\MikeFrancis\LaravelUnleash\Values\Variant\PayloadString`
- `\MikeFrancis\LaravelUnleash\Values\Variant\PayloadDefault` — when no variant is found and the default is used instead

> **Note:** You _can_ combine variants with strategies.

## Blade Templates

Blade directive for checking if a feature is **enabled**:

Expand All @@ -118,9 +130,9 @@ Check back later for more features!
@endfeatureDisabled
```

You cannot currently use dynamic strategy arguments with Blade template directives.
> **Note:** You cannot currently use dynamic strategy arguments with Blade template directives.

### Middleware
## Middleware

This package includes middleware that will deny routes depending on whether a feature is enabled or not.

Expand Down Expand Up @@ -160,4 +172,115 @@ class ExampleController extends Controller
}
```

You cannot currently use dynamic strategy arguments with Middleware.
> **Note:** You cannot currently use dynamic strategy arguments with Middleware.

## Mocking

If you are writing tests against code utilizing feature flags you can mock feature flags using the `Unleash::fake()` method.

As with Unleash itself, the default behavior is for all flags to be considered disabled:

```php
\Unleash::fake();

$this->assertFalse(\Unleash::enabled('any-flag'));
```

To consider all flags enabled, you can set the default to `true` using `withDefaultStatus()`:

```php
\Unleash::fake()->withDefaultStatus(true);

$this->assertTrue(\Unleash::enabled('any-flag'));
```

You may also dynamically return the default status using `withDefaultUsing()`:

```php
\Unleash::fake()->withDefaultStatusUsing(function($flagName, $flagStatus, ... $args) {
return !$args[0];
});

$this->assertTrue(\Unleash::enabled('any-flag', false));

$this->assertFalse(\Unleash::enabled('any-flag', true));
```

Additionaly, there are several ways to set specific Feature Flags to enabled.

To always enable one or more feature flags, you can pass in an array of flag names:

```php
\Unleash::fake(['flag-name-to-enable', 'another-enabled-flag']);

$this->assertTrue(\Unleash::enabled('flag-name-to-enable'));
$this->assertTrue(\Unleash::enabled('another-enabled-flag'));

$this->assertFalse(\Unleash::enabled('an-unknown-flag'));
```

> **Note:** You can call `Unleash::fake()` multiple times in a single test to set additional flags

Alternatively, for more advanced scenarios, you can pass in a variable number of `\MikeFrancis\LaravelUnleash\Values\FeatureFlag` instances.

If you wish to only enable the feature with the correct `DynamicStrategy` arguments without executing the strategy, you can use `withTestArgs()`:

```php
use \MikeFrancis\LaravelUnleash\Values\FeatureFlag;

\Unleash::fake(
(new FeatureFlag('flag-name', true))->withTestArgs(1, 2, 3);
)

$this->assertTrue(\Unleash::enabled('flag-name', 1, 2, 3));

$this->assertFalse(\Unleash::enabled('flag-name'));
$this->assertFalse(\Unleash::enabled('flag-name', 2, 4, 6));
```

If you need to validate the arguments dynamically, you can instead use `withTestArgsUsing()` which takes a callback that returns a boolean on whether the arguments are accepted or not:

```php
use \MikeFrancis\LaravelUnleash\Values\FeatureFlag;

\Unleash::fake(
(new FeatureFlag('flag-name', true))->withTestArgsUsing(function(int $int) {
return $int % 2 === 0;
});
)

$this->assertTrue(\Unleash::enabled('flag-name', 2));
$this->assertTrue(\Unleash::enabled('flag-name', 4));

$this->assertFalse(\Unleash::enabled('flag-name'));
$this->assertFalse(\Unleash::enabled('flag-name', 1));
$this->assertFalse(\Unleash::enabled('flag-name', 3));
```

One final option is the `withTestArgsAny()` which will allow any arguments. This is an alias for the following:

```php
use \MikeFrancis\LaravelUnleash\Values\FeatureFlag;

\Unleash::fake(
(new FeatureFlag('flag-name', true))->withTestArgsUsing(function() {
return true;
});
)
```

We recommend using the `Unleash::fake(['flag-name'])` option instead.

Lastly, you may pass in both Strategies and Variants to the `FeatureFlag` and both will execute as normal:

```php
use \MikeFrancis\LaravelUnleash\Values\FeatureFlag;

\Unleash::fake(
(new FeatureFlag('flag-name', true, '', 'default', false, 'release', [
'myStrategy' => MyStrategyClass::class,
]))->withTestArgsUsing(function() {
return true;
});
)
```
15 changes: 10 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
"description": "An Unleash client for Laravel",
"type": "library",
"require": {
"php": "^8.1|8.2|8.3",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.3|^7.0",
"illuminate/support": "^5.8|^6|^7|^8",
"illuminate/http": "^5.8|^6|^7|^8",
"illuminate/contracts": "^5.8|^6|^7|^8"
"illuminate/support": "^5.8|^6|^7|^8|^9|^10|^11",
"illuminate/config": "^5.8|^6|^7|^8|^9|^10|^11",
"illuminate/http": "^5.8|^6|^7|^8|^9|^10|^11",
"illuminate/contracts": "^5.8|^6|^7|^8|^9|^10|^11",
"lastguest/murmurhash": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "^8.3",
"squizlabs/php_codesniffer": "^3.5"
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.5",
"orchestra/testbench": "^v7.19.0"
},
"autoload": {
"psr-4": {
Expand Down
36 changes: 14 additions & 22 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src</directory>
<exclude>
<file>./src/ServiceProvider.php</file>
</exclude>
</whitelist>
</filter>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<exclude>
<file>./src/ServiceProvider.php</file>
</exclude>
</coverage>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
7 changes: 7 additions & 0 deletions src/Exception/UnknownVariantTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace MikeFrancis\LaravelUnleash\Exception;

class UnknownVariantTypeException extends \Exception
{
}
8 changes: 8 additions & 0 deletions src/Exception/VariantNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace MikeFrancis\LaravelUnleash\Exception;

class VariantNotFoundException extends \Exception
{

}
31 changes: 4 additions & 27 deletions src/Facades/Feature.php
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
<?php
namespace MikeFrancis\LaravelUnleash\Facades;

use Illuminate\Support\Facades\Facade;

class Feature extends Facade
/**
* @inheritDoc
*/
class Feature extends Unleash
{
public static function enabled(string $feature, ...$args): bool
{
return static::isFeatureEnabled($feature, ...$args);
}

public static function disabled(string $feature, ...$args): bool
{
return static::isFeatureDisabled($feature, ...$args);
}

public static function all(): array
{
return static::getFeatures();
}

public static function get(string $name)
{
return static::getFeature($name);
}

protected static function getFacadeAccessor()
{
return 'unleash';
}
}
Loading