Last active
September 3, 2018 14:24
-
-
Save colindecarlo/32af68ac16113848d068114ea0039131 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Vehikl\Feature\Tests; | |
use PHPUnit\Framework\TestCase; | |
class FeatureToggleTest extends TestCase | |
{ | |
public function test_it_executes_the_on_method_when_the_feature_is_on() | |
{ | |
global $enabled; | |
$enabled = true; | |
$this->assertTrue((new SomeClass())->go()); | |
} | |
public function test_it_executes_the_off_method_when_the_feature_is_off() | |
{ | |
global $enabled; | |
$enabled = false; | |
$this->assertFalse((new SomeClass())->go()); | |
} | |
public function test_it_can_figure_out_which_feature_to_use_dynamically() | |
{ | |
global $enabled; | |
$enabled = true; | |
$this->assertTrue((new SomeClass)->goAnotherWay()); | |
} | |
public function test_it_can_figure_out_which_feature_to_use_dynamically_when_there_are_multiple_traits() | |
{ | |
global $enabled; | |
$enabled = false; | |
$this->assertFalse((new SomeClass)->yetAnotherWayToGo()); | |
} | |
} | |
trait WillThisWork | |
{ | |
public function features() { | |
return [ | |
'foobar' => ['on' => 'foo', 'off' => 'bar'] | |
]; | |
} | |
public function enabled() | |
{ | |
global $enabled; | |
return $enabled; | |
} | |
protected function foo() | |
{ | |
return $this->returnTrue(); | |
} | |
protected function bar() | |
{ | |
return $this->returnFalse(); | |
} | |
} | |
trait AnotherFeature | |
{ | |
public function features() { | |
return [ | |
'quxqiz' => ['on' => 'qux', 'off' => 'qiz'] | |
]; | |
} | |
public function enabled() | |
{ | |
global $enabled; | |
return $enabled; | |
} | |
public function qux() | |
{ | |
return true; | |
} | |
public function qiz() | |
{ | |
return false; | |
} | |
} | |
class SomeClass | |
{ | |
use WillThisWork, AnotherFeature { | |
WillThisWork::features insteadof AnotherFeature; | |
WillThisWork::enabled insteadof AnotherFeature; | |
} | |
public function go() | |
{ | |
$fm = new FeatureManager; | |
return $fm->feature(WillThisWork::class)->foobar(); | |
} | |
public function goAnotherWay() | |
{ | |
return FeatureManager::foobar(); | |
} | |
public function yetAnotherWayToGo() | |
{ | |
return FeatureManager::quxqiz(); | |
} | |
protected function returnTrue() | |
{ | |
return true; | |
} | |
protected function returnFalse() | |
{ | |
return false; | |
} | |
} | |
class FeatureManager | |
{ | |
public function feature($feature) | |
{ | |
return $this->getFeature($feature); | |
} | |
protected function getFeature($feature) | |
{ | |
$def = <<<DEF | |
return new class() extends \Vehikl\Feature\Tests\Feature { | |
use $feature; | |
}; | |
DEF; | |
return eval($def); | |
} | |
public function __call($name, $arguments) | |
{ | |
$caller = $this->getCaller(); | |
$traits = (new \ReflectionClass($caller))->getTraits(); | |
foreach($traits as $trait) { | |
if (! $trait->hasMethod('features')) { | |
continue; | |
} | |
$feature = $this->getFeature($trait->getName()); | |
$features = $feature->features(); | |
if (! (is_array($features) && array_key_exists($name, $features))) { | |
continue; | |
} | |
return $feature->__call($name, $arguments); | |
} | |
return false; | |
} | |
public static function __callStatic($name, $arguments) | |
{ | |
return (new static)->__call($name, $arguments); | |
} | |
private function getCaller() | |
{ | |
foreach(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 5) as $trace) { | |
if (in_array(get_class($trace['object'] ?? $this), [Feature::class, FeatureManager::class, get_class($this)])) { | |
continue; | |
} | |
return $trace['object']; | |
} | |
} | |
} | |
abstract class Feature | |
{ | |
public function __call($method, $arguments) | |
{ | |
$caller = $this->getCaller(); | |
$features = $this->features(); | |
$methodToCall = $this->enabled() ? $features[$method]['on'] : $features[$method]['off']; | |
return \Closure::bind(function () use ($methodToCall) { | |
return $this->$methodToCall(); | |
}, $caller, get_class($caller))(); | |
} | |
private function getCaller() | |
{ | |
foreach(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 5) as $trace) { | |
if (in_array(get_class($trace['object'] ?? $this), [Feature::class, FeatureManager::class, get_class($this)])) { | |
continue; | |
} | |
return $trace['object']; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment