Completed
Branch feature/pre-split (afd44c)
by Anton
07:02
created

RuleManager::validateRule()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
c 0
b 0
f 0
cc 6
eloc 9
nc 4
nop 1
rs 8.8571
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Security;
9
10
use Interop\Container\ContainerInterface;
11
use Spiral\Core\Container\SingletonInterface;
12
use Spiral\Security\Exceptions\RuleException;
13
use Spiral\Security\Rules\CallableRule;
14
15
/**
16
 * Provides ability to request permissions rules based on it's name.
17
 */
18
class RuleManager implements RulesInterface, SingletonInterface
19
{
20
    /**
21
     * @var array
22
     */
23
    private $rules = [];
24
25
    /**
26
     * @var ContainerInterface
27
     */
28
    protected $container = null;
29
30
    /**
31
     * RuleManager constructor.
32
     *
33
     * @param ContainerInterface $container
34
     */
35
    public function __construct(ContainerInterface $container)
36
    {
37
        $this->container = $container;
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     *
43
     * @return $this
44
     */
45
    public function set(string $name, $rule = null): RuleManager
46
    {
47
        if (empty($rule)) {
48
            $rule = $name;
49
        }
50
51
        if (!$this->validateRule($rule)) {
52
            throw new RuleException("Unable to set rule '{$name}', invalid rule body");
53
        }
54
55
        $this->rules[$name] = $rule;
56
57
        return $this;
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     *
63
     * @return $this
64
     */
65
    public function remove(string $name): RuleManager
66
    {
67
        if (!$this->has($name)) {
68
            throw new RuleException("Undefined rule '{$name}'");
69
        }
70
71
        unset($this->rules[$name]);
72
73
        return $this;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function has(string $name): bool
80
    {
81
        if (isset($this->rules[$name])) {
82
            return true;
83
        }
84
85
        if (class_exists($name)) {
86
            //We are allowing to use class names without direct registration
87
            return true;
88
        }
89
90
        //Relying on container binding
91
        return $this->container->has($name);
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function get(string $name): RuleInterface
98
    {
99
        if (!$this->has($name)) {
100
            throw new RuleException("Undefined rule '{$name}'");
101
        }
102
103
        if (!isset($this->rules[$name])) {
104
            //Rule represented as class name
105
            $rule = $name;
106
        } else {
107
            $rule = $this->rules[$name];
108
        }
109
110
        if ($rule instanceof RuleInterface) {
111
            return $rule;
112
        }
113
114
        if (is_string($rule)) {
115
            //We are expecting that rule points to
116
            $rule = $this->container->get($rule);
117
118
            if (!$rule instanceof RuleInterface) {
119
                throw new RuleException(
120
                    "Rule '{$name}' must point to RuleInterface, '" . get_class($rule) . "' given"
121
                );
122
            }
123
124
            return $rule;
125
        }
126
127
        //We have to respond using RuleInterface (expecting that rule is callable)
128
        return new CallableRule($rule);
129
    }
130
131
    /**
132
     * @param mixed $rule
133
     *
134
     * @return bool
135
     */
136
    private function validateRule(string $rule): bool
137
    {
138
        if ($rule instanceof \Closure || $rule instanceof RuleInterface) {
139
            return true;
140
        }
141
142
        if (is_array($rule)) {
143
            return is_callable($rule, true);
144
        }
145
146
        if (is_string($rule) && class_exists($rule)) {
147
            $reflection = new \ReflectionClass($rule);
148
149
            return $reflection->isSubclassOf(RuleInterface::class);
150
        }
151
152
        return false;
153
    }
154
}
155