Completed
Pull Request — master (#4)
by Sebastian
13:15
created

Filter::filter()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 3
nop 2
dl 0
loc 17
ccs 9
cts 10
cp 0.9
crap 4.016
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Linna Filter
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Filter;
13
14
use InvalidArgumentException;
15
use Linna\Filter\Rules\RuleInterface;
16
use Linna\Filter\Rules\RuleSanitizeInterface;
17
use Linna\Filter\Rules\RuleValidateInterface;
18
use ReflectionClass;
19
use ReflectionMethod;
20
21
/**
22
 * Filter.
23
 */
24
class Filter
25
{
26
    /**
27
     * @var array User data.
28
     */
29
    private $data = [];
30
31
    /**
32
     * @var array Sanitized data.
33
     */
34
    private $sanitizedData = [];
35
36
    /**
37
     * @var array Error messages.
38
     */
39
    private $messages = [];
40
41
    /**
42
     * @var int Occurred errors.
43
     */
44
    private $errors = 0;
45
46
    /**
47
     * @var array Filters rules.
48
     */
49
    private $rules = [];
50
51
    /**
52
     * @var array Rule aliases.
53
     */
54
    private $alias = [];
55
56
    /**
57
     * Class Constructor.
58
     */
59 84
    public function __construct()
60
    {
61 84
        [$this->rules, $this->alias] = RuleBuilder::build();
62 84
    }
63
64
    /**
65
     * Filter.
66
     *
67
     * @param mixed        $data
68
     * @param array|string $rule
69
     *
70
     * @throws InvalidArgumentException If rule isn't a string when used for only
71
     *                                  one value. If data and rule aren't array
72
     *                                  when used for multi values.
73
     *
74
     * @return object
75
     */
76 84
    public function filter($data, $rule)
77
    {
78 84
        if (is_array($data) && is_array($rule)) {
79 55
            $this->sanitizedData = $this->data = $data;
80 55
            $this->interpreteRules($rule);
81
82 55
            return $this->buildResultObject();
83
        }
84
85 29
        if (is_string($rule)) {
86 29
            $this->sanitizedData = $this->data = ['data' => $data];
87 29
            $this->interpreteRules(['data '.$rule]);
88
89 29
            return $this->buildResultObject();
90
        }
91
92
        throw new InvalidArgumentException('Invalid types passed for data or rules.');
93
    }
94
95
    /**
96
     * Build anonymous class contain results of filtering.
97
     *
98
     * @return object
99
     */
100
    private function buildResultObject()
101
    {
102
        return new class($this->sanitizedData, $this->messages, $this->errors) {
103
            private $data;
104
            private $message;
105
            private $error;
106
107 84
            public function __construct(array $data, array $message, int $error)
108
            {
109 84
                $this->data = $data;
110 84
                $this->message = $message;
111 84
                $this->error = $error;
112 84
            }
113
114 1
            public function data()
115
            {
116 1
                return $this->data;
117
            }
118
119
            public function messages()
120
            {
121
                return $this->message;
122
            }
123
124 27
            public function errors()
125
            {
126 27
                return $this->error;
127
            }
128
        };
129
    }
130
131
    /**
132
     * Return occurred error number.
133
     *
134
     * @return int
135
     */
136 57
    public function getErrors(): int
137
    {
138 57
        return $this->errors;
139
    }
140
141
    /**
142
     * Return error messages.
143
     *
144
     * @return array
145
     */
146 25
    public function getMessages(): array
147
    {
148 25
        return $this->messages;
149
    }
150
151
    /**
152
     * Return passed data.
153
     *
154
     * @return array
155
     */
156 6
    public function getData(): array
157
    {
158 6
        return $this->sanitizedData;
159
    }
160
161
    /**
162
     * Get parsed rules.
163
     */
164 84
    private function interpreteRules($rules): void
165
    {
166 84
        $parser = new Parser();
167 84
        $lexer = new Lexer();
168
169 84
        foreach ($rules as $rule) {
170 84
            $this->ruleToField(
171 84
                $parser->parse(
172 84
                    $lexer->tokenize($rule),
173 84
                    $this->rules,
174 84
                    $this->alias
175
                )
176
            );
177
        }
178 84
    }
179
180
    /**
181
     * Apply rules to a field.
182
     *
183
     * @param array $rules
184
     */
185 84
    private function ruleToField(array $rules): void
186
    {
187 84
        foreach ($rules as $rule) {
188 84
            $field = $rule[0];
189 84
            $ruleProps = $this->rules[$rule[1]];
190 84
            $ruleParams = $rule[2];
191
192 84
            $instance = new $ruleProps['full_class']();
193
194
            //initialize message array
195 84
            $this->messages[$field] = $this->messages[$field] ?? [];
196
197
            //check if value is isset in data
198 84
            if ($this->checkValue($field, $ruleProps['class'])) {
199 3
                continue;
200
            }
201
202
            //invoke sanitize section of the filter
203
            //if filter fail go to next rule
204 82
            if ($this->invokeValidate($instance, $field, $ruleParams)) {
205 47
                continue;
206
            }
207
208
            //invoke sanitize section of the filter
209 41
            if ($instance instanceof RuleSanitizeInterface) {
210 41
                $instance->sanitize($this->sanitizedData[$field]);
211
            }
212
        }
213 84
    }
214
215
    /**
216
     * Check if a field exist in data.
217
     *
218
     * @param string $field
219
     * @param string $filter
220
     *
221
     * @return bool
222
     */
223 84
    private function checkValue(string &$field, string &$filter): bool
0 ignored issues
show
Unused Code introduced by
The parameter $filter is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

223
    private function checkValue(string &$field, /** @scrutinizer ignore-unused */ string &$filter): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
224
    {
225 84
        if (isset($this->data[$field])) {
226 82
            return false;
227
        }
228
229 3
        $this->errors++;
230 3
        $this->messages[$field][] = "Form field '{$field}' missing.";
231
232 3
        return true;
233
    }
234
235
    /**
236
     * Invoke validate.
237
     *
238
     * @param RuleInterface $instance
239
     * @param string        $field
240
     * @param array         $ruleParams
241
     *
242
     * @return bool
243
     */
244 82
    private function invokeValidate(RuleInterface &$instance, string $field, array $ruleParams): bool
245
    {
246 82
        if (!($instance instanceof RuleValidateInterface)) {
247
            return true;
248
        }
249
250 82
        array_unshift($ruleParams, $this->data[$field]);
251
252 82
        if (call_user_func_array(array($instance, 'validate'), $ruleParams)) {
253 47
            $this->errors++;
254
255 47
            $message = $instance->getMessage();
256
257 47
            if (strlen($message)) {
258 47
                $this->messages[$field][] = $message;
259
            }
260
261 47
            return true;
262
        }
263
264 41
        return false;
265
    }
266
}
267