Completed
Branch feature/pre-split (f4dde1)
by Anton
03:26
created

RuleManager::validRule()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 9
nc 4
nop 1
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
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\Core\FactoryInterface;
13
use Spiral\Security\Exceptions\RuleException;
14
use Spiral\Security\Rules\CallableRule;
15
use Spiral\Support\Patternizer;
16
17
/**
18
 * Provides ability to request permissions rules based on it's name.
19
 */
20
class RuleManager implements RulesInterface, SingletonInterface
21
{
22
    /**
23
     * @var array
24
     */
25
    private $rules = [];
26
27
    /**
28
     * @var Patternizer
29
     */
30
    private $patternizer = null;
31
32
    /**
33
     * @var ContainerInterface
34
     */
35
    protected $container = null;
36
37
    /**
38
     * RuleManager constructor.
39
     *
40
     * @param FactoryInterface $container
41
     * @param Patternizer|null $patternizer
42
     */
43
    public function __construct(FactoryInterface $container, Patternizer $patternizer)
44
    {
45
        $this->container = $container;
0 ignored issues
show
Documentation Bug introduced by
It seems like $container of type object<Spiral\Core\FactoryInterface> is incompatible with the declared type object<Interop\Container\ContainerInterface> of property $container.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
46
        $this->patternizer = $patternizer;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     *
52
     * @return $this
53
     */
54
    public function set(string $name, $rule = null): RuleManager
55
    {
56
        if (empty($rule)) {
57
            $rule = $name;
58
        }
59
60
        if (!$this->validRule($rule)) {
61
            throw new RuleException("Unable to set rule '{$name}', invalid rule body");
62
        }
63
64
        $this->rules[$name] = $rule;
65
66
        return $this;
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     *
72
     * @return $this
73
     */
74
    public function remove(string $name): RuleManager
75
    {
76
        if (!$this->has($name)) {
77
            throw new RuleException("Undefined rule '{$name}'");
78
        }
79
80
        unset($this->rules[$name]);
81
82
        return $this;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function has(string $name): bool
89
    {
90
        if (isset($this->rules[$name])) {
91
            return true;
92
        }
93
94
        if (class_exists($name)) {
95
            //We are allowing to use class names without direct registration
96
            return true;
97
        }
98
99
        //Relying on container binding
100
        return $this->container->has($name);
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function get(string $name): RuleInterface
107
    {
108
        if (!$this->has($name)) {
109
            throw new RuleException("Undefined rule '{$name}'");
110
        }
111
112
        if (!isset($this->rules[$name])) {
113
            //Rule represented as class name
114
            $rule = $name;
115
        } else {
116
            $rule = $this->rules[$name];
117
        }
118
119
        if ($rule instanceof RuleInterface) {
120
            return $rule;
121
        }
122
123
        if (is_string($rule)) {
124
            //We are expecting that rule points to
125
            $rule = $this->container->get($rule);
126
127
            if (!$rule instanceof RuleInterface) {
128
                throw new RuleException(
129
                    "Rule '{$name}' must point to RuleInterface, '" . get_class($rule) . "' given"
130
                );
131
            }
132
133
            return $rule;
134
        }
135
136
        //We have to respond using RuleInterface (expecting that rule is callable)
137
        return new CallableRule($rule);
138
    }
139
140
    /**
141
     * @param mixed $rule
142
     *
143
     * @return bool
144
     */
145
    private function validRule(string $rule): bool
146
    {
147
        if ($rule instanceof \Closure || $rule instanceof RuleInterface) {
148
            return true;
149
        }
150
151
        if (is_array($rule)) {
152
            return is_callable($rule, true);
153
        }
154
155
        if (is_string($rule) && class_exists($rule)) {
156
            $reflection = new \ReflectionClass($rule);
157
158
            return $reflection->isSubclassOf(RulesInterface::class);
159
        }
160
161
        return false;
162
    }
163
}
164