Sanitizer::hasRegistrar()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
nc 1
cc 1
eloc 2
nop 0
crap 1
1
<?php
2
3
namespace Alfheim\Sanitizer;
4
5
use InvalidArgumentException;
6
use Alfheim\Sanitizer\Registrar\RegistrarInterface;
7
8
class Sanitizer
9
{
10
    /**
11
     * The value placeholder string. This allows the user to change the
12
     * position of the value to sanitize in the arguments array.
13
     *
14
     * @var string
15
     */
16
    const PLACEHOLDER_VALUE = '{{ VALUE }}';
17
18
    /**
19
     * The global keyword.
20
     *
21
     * @var string
22
     */
23
    const GLOBAL_KEY = '*';
24
25
    /** @var array */
26
    protected $rules = [];
27
28
    /** @var \Alfheim\Sanitizer\Registrar\RegistrarInterface */
29
    protected $registrar;
30
31
    /**
32
     * Statically create a new Sanitizer instance with a set of rules.
33
     *
34
     * @param  string|array  $ruleset  The sanitation rules.
35
     *
36
     * @return \Alfheim\Sanitizer\Sanitizer
37
     *
38
     * @static
39
     */
40 96
    public static function make($ruleset)
41
    {
42 96
        return (new static)->rules($ruleset);
43
    }
44
45
    /**
46
     * Add rules to the sanitizer.
47
     *
48
     * @param  string|array  $ruleset  The sanitation rules.
49
     *
50
     * @return \Alfheim\Sanitizer\Sanitizer  $this
51
     */
52 132
    public function rules($ruleset)
53
    {
54 132
        if (! is_array($ruleset)) {
55 48
            $ruleset = [static::GLOBAL_KEY => $ruleset];
56 36
        }
57
58 132
        foreach ($ruleset as $key => $rules) {
59 132
            $this->addRule($key, $rules);
60 99
        }
61
62 132
        return $this;
63
    }
64
65
    /**
66
     * Sanitize some data.
67
     *
68
     * @param  mixed  $data
69
     *
70
     * @return mixed
71
     */
72 128
    public function sanitize($data)
73
    {
74 128
        if ($this->hasGlobals()) {
75 60
            if (! is_array($data)) {
76 48
                return $this->sanitizeValueFor(static::GLOBAL_KEY, $data);
77
            }
78
79 12
            foreach ($data as $key => $value) {
80 12
                $data[$key] = $this->sanitizeValueFor(static::GLOBAL_KEY, $value);
81 9
            }
82 9
        }
83
84 80
        foreach ($data as $key => $value) {
85 80
            if (! $this->shouldSanitize($key)) {
86 28
                continue;
87
            }
88
89 72
            $data[$key] = $this->sanitizeValueFor($key, $value);
90 57
        }
91
92 76
        return $data;
93
    }
94
95
    /**
96
     * Sanitize some data by reference.
97
     *
98
     * @param  mixed  &$data
99
     */
100 8
    public function sanitizeByRef(&$data)
101
    {
102 8
        $data = $this->sanitize($data);
103 8
    }
104
105
    /**
106
     * Set the registrar instance for the sanitizer.
107
     *
108
     * @param  \Alfheim\Sanitizer\Registrar\RegistrarInterface  $registrar
109
     *
110
     * @return \Alfheim\Sanitizer\Sanitizer  $this
111
     */
112 28
    public function setRegistrar(RegistrarInterface $registrar)
113
    {
114 28
        $this->registrar = $registrar;
115
116 28
        return $this;
117
    }
118
119
    /**
120
     * Check if a registrar has been set.
121
     *
122
     * @return bool
123
     */
124 120
    protected function hasRegistrar()
125
    {
126 120
        return ! is_null($this->registrar);
127
    }
128
129
    /**
130
     * Check if global rules have been registered.
131
     *
132
     * @return bool
133
     */
134 128
    protected function hasGlobals()
135
    {
136 128
        return isset($this->rules[static::GLOBAL_KEY]);
137
    }
138
139
    /**
140
     * Check if a given key should be sanitized.
141
     *
142
     * @param  string  $key
143
     *
144
     * @return bool
145
     */
146 132
    protected function shouldSanitize($key)
147
    {
148 132
        return isset($this->rules[$key]);
149
    }
150
151
    /**
152
     * Sanitize a single value for a given key.
153
     *
154
     * @param  string  $key
155
     * @param  mixed   $value
156
     *
157
     * @return mixed
158
     */
159 128
    protected function sanitizeValueFor($key, $value)
160
    {
161 128
        foreach ($this->rules[$key] as $rule) {
162 128
            $value = call_user_func_array(
163 128
                $this->getCallable($rule[0], $key),
164 124
                $this->buildArguments($value, isset($rule[1]) ? $rule[1] : null)
165 93
            );
166 93
        }
167
168 124
        return $value;
169
    }
170
171
    /**
172
     * Resolve the callable for a given rule.
173
     *
174
     * @param  mixed   $value
175
     * @param  string  $key
176
     *
177
     * @return callable
178
     *
179
     * @throws \InvalidArgumentException
180
     */
181 128
    protected function getCallable($value, $key)
182
    {
183
        // If the value is a string, a registrar is set and the value is
184
        // registred with the registrar, resolve it there.
185
        if (
186 128
            is_string($value) &&
187 128
            $this->hasRegistrar() &&
188 53
            $this->registrar->isRegistred($value)
189 96
        ) {
190 28
            $value = $this->registrar->resolve($value);
191 21
        }
192
193
        // If the value is now a callable, go ahead and return it...
194 128
        if (is_callable($value)) {
195 120
            return $value;
196
        }
197
198
        // However, if we've got an object and a filter method for the key
199
        // exists, we'll return that as a callable.
200
        if (
201 8
            is_object($value) &&
202 5
            method_exists($value, $method = $this->getFilterMethodName($key))
203 6
        ) {
204 4
            return [$value, $method];
205
        }
206
207 4
        throw new InvalidArgumentException(sprintf(
208 4
            'Could not resolve callable for [%s]', $value
209 3
        ));
210
    }
211
212
    /**
213
     * Build the arguments for a callback.
214
     *
215
     * @param  mixed       $value
216
     * @param  array|null  $args
217
     *
218
     * @return array
219
     */
220 124
    protected function buildArguments($value, array $args = null)
221
    {
222 124
        if (! $args) {
223 116
            return (array) $value;
224
        }
225
226 28
        $valuePosition = array_search(static::PLACEHOLDER_VALUE, $args, true);
227
228 28
        if ($valuePosition === false) {
229 4
            return array_merge((array) $value, $args);
230
        } else {
231 24
            $args[$valuePosition] = $value;
232
        }
233
234 24
        return $args;
235
    }
236
237
    /**
238
     * Add a rule to the sanitizer factory.
239
     *
240
     * @param  string  $key
241
     * @param  string|array|\Closure  $rules
242
     *
243
     * @return void
244
     *
245
     * @throws \InvalidArgumentException
246
     */
247 132
    protected function addRule($key, $rules)
248
    {
249 132
        if ($this->shouldSanitize($key)) {
250 4
            throw new InvalidArgumentException(sprintf(
251 4
                'Sanitation rules are already defined for field [%s]', $key
252 3
            ));
253
        }
254
255 132
        $this->rules[$key] = $this->buildRules($rules);
256 132
    }
257
258
    /**
259
     * Build a valid set of rules.
260
     *
261
     * @param  string|array|\Closure  $rules
262
     *
263
     * @return array
264
     */
265 132
    protected function buildRules($rules)
266
    {
267 132
        if (is_string($rules)) {
268 108
            $rules = explode('|', $rules);
269 105
        } elseif (is_object($rules)) {
270 8
            $rules = [$rules];
271 6
        }
272
273 132
        $built = [];
274
275 132
        foreach ((array) $rules as $rule) {
276 132
            if (is_string($rule) && strpos($rule, ':') !== false) {
277 28
                $args = explode(':', $rule);
278 28
                $rule = array_shift($args);
279
280 28
                $built[] = [$rule, $args];
281 21
            } else {
282 126
                $built[] = [$rule];
283
            }
284 99
        }
285
286 132
        return $built;
287
    }
288
289
    /**
290
     * Get the filter method name which will be called on an object.
291
     *
292
     * @param  string  $value
293
     *
294
     * @return string
295
     */
296 4
    protected function getFilterMethodName($value)
297
    {
298 4
        return sprintf('filter%s', lcfirst(
299 4
            str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $value)))
300 3
        ));
301
    }
302
}
303