Completed
Branch feature/pre-split (197e7e)
by Anton
03:26
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
use Spiral\Support\Patternizer;
15
16
/**
17
 * Provides ability to request permissions rules based on it's name.
18
 */
19
class RuleManager implements RulesInterface, SingletonInterface
20
{
21
    /**
22
     * @var array
23
     */
24
    private $rules = [];
25
26
    /**
27
     * @var Patternizer
28
     */
29
    private $patternizer = null;
30
31
    /**
32
     * @var ContainerInterface
33
     */
34
    protected $container = null;
35
36
    /**
37
     * RuleManager constructor.
38
     *
39
     * @param ContainerInterface $container
40
     * @param Patternizer|null   $patternizer
41
     */
42
    public function __construct(ContainerInterface $container, Patternizer $patternizer)
43
    {
44
        $this->container = $container;
45
        $this->patternizer = $patternizer;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     *
51
     * @return $this
52
     */
53
    public function set(string $name, $rule = null): RuleManager
54
    {
55
        if (empty($rule)) {
56
            $rule = $name;
57
        }
58
59
        if (!$this->validateRule($rule)) {
60
            throw new RuleException("Unable to set rule '{$name}', invalid rule body");
61
        }
62
63
        $this->rules[$name] = $rule;
64
65
        return $this;
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     *
71
     * @return $this
72
     */
73
    public function remove(string $name): RuleManager
74
    {
75
        if (!$this->has($name)) {
76
            throw new RuleException("Undefined rule '{$name}'");
77
        }
78
79
        unset($this->rules[$name]);
80
81
        return $this;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function has(string $name): bool
88
    {
89
        if (isset($this->rules[$name])) {
90
            return true;
91
        }
92
93
        if (class_exists($name)) {
94
            //We are allowing to use class names without direct registration
95
            return true;
96
        }
97
98
        return false;
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function get(string $name): RuleInterface
105
    {
106
        if (!$this->has($name)) {
107
            throw new RuleException("Undefined rule '{$name}'");
108
        }
109
110
        if (!isset($this->rules[$name])) {
111
            //Rule represented as class name
112
            $rule = $name;
113
        } else {
114
            $rule = $this->rules[$name];
115
        }
116
117
        if ($rule instanceof RuleInterface) {
118
            return $rule;
119
        }
120
121
        if (is_string($rule)) {
122
            //We are expecting that rule points to
123
            $rule = $this->container->get($rule);
124
125
            if (!$rule instanceof RuleInterface) {
126
                throw new RuleException(
127
                    "Rule '{$name}' must point to RuleInterface, '" . get_class($rule) . "' given"
128
                );
129
            }
130
131
            return $rule;
132
        }
133
134
        //We have to respond using RuleInterface (expecting that rule is callable)
135
        return new CallableRule($rule);
136
    }
137
138
    /**
139
     * @param mixed $rule
140
     *
141
     * @return bool
142
     */
143
    private function validateRule($rule): bool
144
    {
145
        if ($rule instanceof \Closure || $rule instanceof RuleInterface) {
146
            return true;
147
        }
148
149
        if (is_array($rule)) {
150
            return is_callable($rule, true);
151
        }
152
153
        if (is_string($rule) && class_exists($rule)) {
154
            $reflection = new \ReflectionClass($rule);
155
156
            return $reflection->isSubclassOf(RulesInterface::class);
157
        }
158
159
        return false;
160
    }
161
}
162