Skip to content

Commit 5bb3319

Browse files
authored
Merge pull request #17 from bestit/feature/16-blacklist-mode
#16 Add new blacklist mode for CookieActivator
2 parents fad815f + 9187923 commit 5bb3319

File tree

6 files changed

+212
-3
lines changed

6 files changed

+212
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## [1.4.0]
2+
### Added
3+
- \#16 Add new blacklist mode for [CookieActivator](docs/activator/cookie.md) @migo315
4+
15
## [1.3.0]
26
### Added
37
- \#1 \#4 Add new [CacheActivator](docs/activator/cache.md) @migo315

docs/activator/cookie.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,41 @@ Example:
6868
}
6969
```
7070

71+
The CookieActivator acts as whitelist. You can change this behavior to a blacklist if you set the fourth argument.
72+
73+
Example:
74+
75+
```php
76+
// MyClass.php
77+
class MyClass
78+
{
79+
public function doSomething()
80+
{
81+
$activator = new CookieActivator([
82+
'feature_abc',
83+
'feature_ghi'
84+
], 'my_cookie_name', '|', CookieActivator::BLACKLIST);
85+
86+
$manager = new FeatureManager($activator);
87+
88+
// Will return true, if you have set a cookie called "my_cookie_name" with value "feature_wxy|feature_ghi")
89+
if ($manager->isActive('feature_wxy')) {
90+
// do something
91+
}
92+
93+
// Will return always false, because the feature is blacklisted
94+
if ($manager->isActive('feature_abc')) {
95+
// do something
96+
}
97+
98+
// Will return always false, because the feature is blacklisted
99+
if ($manager->isActive('feature_ghi')) {
100+
// do something
101+
}
102+
}
103+
}
104+
```
105+
71106
### Beware!
72107
Cookies are not secure. You should only use this activator for internal stuff. Do not use this for critical or public parts
73108
of your project.

src/Activator/CookieActivator.php

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Flagception\Activator;
44

5+
use Flagception\Exception\InvalidArgumentException;
56
use Flagception\Model\Context;
67

78
/**
@@ -13,7 +14,17 @@
1314
class CookieActivator implements FeatureActivatorInterface
1415
{
1516
/**
16-
* Allowed features
17+
* Activator should act as whitelist
18+
*/
19+
const WHITELIST = 'whitelist';
20+
21+
/**
22+
* Activator should act as blacklist
23+
*/
24+
const BLACKLIST = 'blacklist';
25+
26+
/**
27+
* Features collection
1728
*
1829
* @var array
1930
*/
@@ -33,18 +44,38 @@ class CookieActivator implements FeatureActivatorInterface
3344
*/
3445
private $separator;
3546

47+
/**
48+
* Mode (whitelist / blacklist)
49+
*
50+
* @var string
51+
*/
52+
private $mode;
53+
3654
/**
3755
* CookieActivator constructor.
3856
*
3957
* @param array $features
4058
* @param string $name
4159
* @param string $separator
60+
* @param string $mode
61+
*
62+
* @throws InvalidArgumentException
4263
*/
43-
public function __construct(array $features, $name = 'flagception', $separator = ',')
64+
public function __construct(array $features, $name = 'flagception', $separator = ',', $mode = self::WHITELIST)
4465
{
66+
if (!in_array($mode, [self::BLACKLIST, self::WHITELIST], true)) {
67+
throw new InvalidArgumentException(
68+
sprintf(
69+
'Invalid mode argument for cookie activator: "%s". Expected: "whitelist" or "blacklist"',
70+
$mode
71+
)
72+
);
73+
}
74+
4575
$this->features = $features;
4676
$this->name = $name;
4777
$this->separator = $separator;
78+
$this->mode = $mode;
4879
}
4980

5081
/**
@@ -60,14 +91,22 @@ public function getName()
6091
*/
6192
public function isActive($name, Context $context)
6293
{
63-
if (!in_array($name, $this->features, true)) {
94+
// Disable features which aren't whitelisted
95+
if ($this->mode === self::WHITELIST && !in_array($name, $this->features, true)) {
96+
return false;
97+
}
98+
99+
// Disable features which are blacklisted
100+
if ($this->mode === self::BLACKLIST && in_array($name, $this->features, true)) {
64101
return false;
65102
}
66103

104+
// Disable if non cookie exists
67105
if (!array_key_exists($this->name, $_COOKIE)) {
68106
return false;
69107
}
70108

109+
// Enable, if feature is set in cookie
71110
return in_array($name, array_map('trim', explode($this->separator, $_COOKIE[$this->name])), true);
72111
}
73112
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Flagception\Exception;
4+
5+
/**
6+
* Class InvalidArgumentException
7+
*
8+
* @author Michel Chowanski <[email protected]>
9+
* @package Flagception\Exception
10+
*/
11+
class InvalidArgumentException extends FlagceptionException
12+
{
13+
}

tests/Activator/CookieActivatorTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Flagception\Activator\CookieActivator;
66
use Flagception\Activator\FeatureActivatorInterface;
7+
use Flagception\Exception\InvalidArgumentException;
78
use Flagception\Model\Context;
89
use PHPUnit\Framework\TestCase;
910

@@ -30,6 +31,8 @@ protected function setUp()
3031
* Test implement interface
3132
*
3233
* @return void
34+
*
35+
* @throws InvalidArgumentException
3336
*/
3437
public function testImplementInterface()
3538
{
@@ -40,6 +43,8 @@ public function testImplementInterface()
4043
* Test name
4144
*
4245
* @return void
46+
*
47+
* @throws InvalidArgumentException
4348
*/
4449
public function testName()
4550
{
@@ -51,6 +56,8 @@ public function testName()
5156
* Test feature is unknown
5257
*
5358
* @return void
59+
*
60+
* @throws InvalidArgumentException
5461
*/
5562
public function testFeatureIsUnknown()
5663
{
@@ -64,6 +71,8 @@ public function testFeatureIsUnknown()
6471
* Test feature is known
6572
*
6673
* @return void
74+
*
75+
* @throws InvalidArgumentException
6776
*/
6877
public function testFeatureIsKnown()
6978
{
@@ -79,6 +88,8 @@ public function testFeatureIsKnown()
7988
* Test multiple features
8089
*
8190
* @return void
91+
*
92+
* @throws InvalidArgumentException
8293
*/
8394
public function testMultipleFeatures()
8495
{
@@ -101,6 +112,8 @@ public function testMultipleFeatures()
101112
* Test changed cookie name is unknown
102113
*
103114
* @return void
115+
*
116+
* @throws InvalidArgumentException
104117
*/
105118
public function testCookieNameChangeUnknown()
106119
{
@@ -116,6 +129,8 @@ public function testCookieNameChangeUnknown()
116129
* Test changed cookie name is known
117130
*
118131
* @return void
132+
*
133+
* @throws InvalidArgumentException
119134
*/
120135
public function testCookieNameChangeKnown()
121136
{
@@ -131,6 +146,8 @@ public function testCookieNameChangeKnown()
131146
* Test multiple features with changed separator
132147
*
133148
* @return void
149+
*
150+
* @throws InvalidArgumentException
134151
*/
135152
public function testMultipleFeaturesWithChangedSeparator()
136153
{
@@ -148,4 +165,79 @@ public function testMultipleFeaturesWithChangedSeparator()
148165
static::assertFalse($activator->isActive('foobar', new Context()));
149166
static::assertFalse($activator->isActive('feature_xyz', new Context()));
150167
}
168+
169+
/**
170+
* Test multiple features by blacklist
171+
*
172+
* @return void
173+
*
174+
* @throws InvalidArgumentException
175+
*/
176+
public function testMultipleFeaturesByBlacklist()
177+
{
178+
$activator = new CookieActivator([
179+
'feature_abc',
180+
'feature_def',
181+
'feature_ghi',
182+
'feature_xyz'
183+
], 'flagception', ',', CookieActivator::BLACKLIST);
184+
185+
$_COOKIE['flagception'] = 'feature_abc,feature_def, feature_ghi, foobar';
186+
static::assertFalse($activator->isActive('feature_abc', new Context()));
187+
static::assertFalse($activator->isActive('feature_def', new Context()));
188+
static::assertFalse($activator->isActive('feature_ghi', new Context()));
189+
static::assertTrue($activator->isActive('foobar', new Context()));
190+
static::assertFalse($activator->isActive('feature_xyz', new Context()));
191+
}
192+
193+
/**
194+
* Test public constants
195+
*
196+
* @return void
197+
*/
198+
public function testConstants()
199+
{
200+
static::assertEquals('whitelist', CookieActivator::WHITELIST);
201+
static::assertEquals('blacklist', CookieActivator::BLACKLIST);
202+
}
203+
204+
/**
205+
* Test if "whitelist" is a valid argument as mode
206+
*
207+
* @return void
208+
*
209+
* @throws InvalidArgumentException
210+
*/
211+
public function testWhitelistModeArgument()
212+
{
213+
$activator = new CookieActivator([], 'flagception', ',', 'whitelist');
214+
static::assertEquals('cookie', $activator->getName());
215+
}
216+
217+
/**
218+
* Test if "blacklist" is a valid argument as mode
219+
*
220+
* @return void
221+
*
222+
* @throws InvalidArgumentException
223+
*/
224+
public function testBlacklistModeArgument()
225+
{
226+
$activator = new CookieActivator([], 'flagception', ',', 'blacklist');
227+
static::assertEquals('cookie', $activator->getName());
228+
}
229+
230+
/**
231+
* Test if "foobar" is an invalid argument as mode
232+
*
233+
* @return void
234+
*
235+
* @throws InvalidArgumentException
236+
*/
237+
public function testFoobarModeArgument()
238+
{
239+
$this->expectException(InvalidArgumentException::class);
240+
241+
new CookieActivator([], 'flagception', ',', 'foobar');
242+
}
151243
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Flagception\Tests\Exception;
4+
5+
use Flagception\Exception\InvalidArgumentException;
6+
use Flagception\Exception\FlagceptionException;
7+
use PHPUnit\Framework\TestCase;
8+
9+
/**
10+
* Class InvalidArgumentExceptionTest
11+
*
12+
* @author Michel Chowanski <[email protected]>
13+
* @package Flagception\Tests\Exception
14+
*/
15+
class InvalidArgumentExceptionTest extends TestCase
16+
{
17+
/**
18+
* Test extends from base exception
19+
*
20+
* @return void
21+
*/
22+
public function testExtends()
23+
{
24+
static::assertInstanceOf(FlagceptionException::class, new InvalidArgumentException());
25+
}
26+
}

0 commit comments

Comments
 (0)