Feature   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 17
lcom 2
cbo 3
dl 0
loc 104
c 0
b 0
f 0
ccs 42
cts 42
cp 1
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A new() 0 4 1
toggles() 0 1 ?
A registerResolver() 0 4 1
A resolver() 0 8 2
A hasToggle() 0 4 1
A caller() 0 4 1
A methodToCall() 0 11 3
A __call() 0 14 2
A __callStatic() 0 10 1
A alwaysOn() 0 4 1
A alwaysOff() 0 4 1
A hasForcedState() 0 4 1
A isAlwaysOn() 0 4 1
1
<?php
2
3
namespace Vehikl\Flip;
4
5
/**
6
 * @method boolean enabled(...$params)
7
 */
8
abstract class Feature
9
{
10
    const ENABLED = 'enabled';
11
    const DISABLED = 'disabled';
12
13
    private static $resolver;
14
    protected $caller;
15
    protected static $forceState;
16
17
    // Maybe it's worth requiring an interface be applied?
18 46
    public function __construct($caller)
19
    {
20 46
        $this->caller = $caller;
21 46
    }
22
23 16
    public static function new($caller): Feature
24
    {
25 16
        return new static($caller);
26
    }
27
28
    abstract public function toggles(): array;
29
30 4
    public static function registerResolver($resolver): void
31
    {
32 4
        self::$resolver = $resolver;
33 4
    }
34
35 36
    public function resolver(): Resolver
36
    {
37 36
        if (! self::$resolver) {
38 2
            self::registerResolver(new DefaultResolver);
39
        }
40
41 36
        return self::$resolver;
42
    }
43
44 10
    public function hasToggle(string $method): bool
45
    {
46 10
        return array_key_exists($method, $this->toggles());
47
    }
48
49 8
    protected function caller()
50
    {
51 8
        return $this->caller;
52
    }
53
54 34
    private function methodToCall(string $toggle): string
55
    {
56 34
        $toggles = $this->toggles();
57
58 34
        if (array_key_exists($toggle, $toggles)) {
59
            // if $toggles was a class, it'd be a lot less error prone.
60 32
            return $this->resolver()->resolve($this, 'enabled') ? $toggles[$toggle]['on'] : $toggles[$toggle]['off'];
61
        }
62
63 8
        return $toggle;
64
    }
65
66 34
    public function __call($name, $arguments)
67
    {
68 34
        $methodToCall = $this->methodToCall($name);
69
70 34
        if (method_exists($this, $methodToCall)) {
71 32
            return $this->{$methodToCall}(...$arguments);
72
        }
73
74
        // Probably easier to just expect a public method.
75 8
        $name = (new \ReflectionClass($this->caller()))->getMethod($methodToCall);
76 4
        $name->setAccessible(true);
77
78 4
        return $name->invoke($this->caller(), $arguments);
79
    }
80
81 2
    public static function __callStatic($method, $arguments)
82
    {
83
        // Could be extracted, but I wonder how reliable this would be?
84
        // Does it really improve the API that much?
85 2
        $caller = Caller::guess();
86
87 2
        $instance = new static($caller);
88
89 2
        return $instance->{$method}($arguments);
90
    }
91
92 4
    public static function alwaysOn() : void
93
    {
94 4
        static::$forceState = self::ENABLED;
95 4
    }
96
97 4
    public static function alwaysOff() : void
98
    {
99 4
        static::$forceState = self::DISABLED;
100 4
    }
101
102 32
    public function hasForcedState() : bool
103
    {
104 32
        return static::$forceState !== null;
105
    }
106
107 8
    public function isAlwaysOn() : bool
108
    {
109 8
        return static::$forceState === self::ENABLED;
110
    }
111
}
112