Completed
Push — b0.3.0 ( 326260...c79ea7 )
by Sebastian
02:52
created

Filter::addCustomRules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 2
rs 10
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\CustomRule;
16
use Linna\Filter\Rules\RuleSanitizeInterface;
17
use Linna\Filter\Rules\RuleValidateInterface;
18
use Linna\Filter\Result;
19
20
/**
21
 * Filter.
22
 */
23
class Filter
24
{
25
    /**
26
     * @var array User data.
27
     */
28
    private $data = [];
29
30
    /**
31
     * @var array Sanitized data.
32
     */
33
    private $sanitizedData = [];
34
35
    /**
36
     * @var array Error messages.
37
     */
38
    private $messages = [];
39
40
    /**
41
     * @var int Occurred errors.
42
     */
43
    private $errors = 0;
44
45
    /**
46
     * @var array Filters rules.
47
     */
48
    private $rules = [];
49
50
    /**
51
     * @var array Rule aliases.
52
     */
53
    private $alias = [];
54
55
    /**
56
     * Class Constructor.
57
     */
58 81
    public function __construct()
59
    {
60 81
        [$this->rules, $this->alias] = RuleBuilder::build();
61 81
    }
62
63
    /**
64
     * Add custom rules to filter.
65
     *
66
     * @param array $customRules
67
     */
68
    public function addCustomRules(array $customRules): void
69
    {
70
        [$rules, $alias] = RuleBuilder::buildCustom($customRules);
71
72
        $this->rules = array_merge($this->rules, $rules);
73
        $this->alias = array_merge($this->alias, $alias);
74
    }
75
76
    /**
77
     * Filter.
78
     *
79
     * @param mixed        $data
80
     * @param array|string $rule
81
     *
82
     * @throws InvalidArgumentException If rule isn't a string when used for only
83
     *                                  one value. If data and rule aren't array
84
     *                                  when used for multi values.
85
     *
86
     * @return Result
87
     */
88 81
    public function filter($data, $rule): Result
89
    {
90 81
        if (is_array($data) && is_array($rule)) {
91 55
            $this->sanitizedData = $this->data = $data;
92 55
            $this->interpreteRules($rule);
93
94 55
            return $this->buildResultObject();
95
        }
96
97 26
        if (is_string($rule)) {
98 25
            $this->sanitizedData = $this->data = ['data' => $data];
99 25
            $this->interpreteRules(['data '.$rule]);
100
101 25
            return $this->buildResultObject();
102
        }
103
104 1
        throw new InvalidArgumentException('Invalid types passed for data or rules.');
105
    }
106
107
    /**
108
     * Build anonymous class contain results of filtering.
109
     *
110
     * @return Result
111
     */
112
    private function buildResultObject(): Result
113
    {
114
        return new class($this->sanitizedData, $this->messages, $this->errors) extends Result {
115
        };
116
    }
117
118
    /**
119
     * Return occurred error number.
120
     *
121
     * @return int
122
     */
123 53
    public function getErrors(): int
124
    {
125 53
        return $this->errors;
126
    }
127
128
    /**
129
     * Return error messages.
130
     *
131
     * @return array
132
     */
133 26
    public function getMessages(): array
134
    {
135 26
        return $this->messages;
136
    }
137
138
    /**
139
     * Return passed data.
140
     *
141
     * @return array
142
     */
143 2
    public function getData(): array
144
    {
145 2
        return $this->sanitizedData;
146
    }
147
148
    /**
149
     * Get parsed rules.
150
     */
151 80
    private function interpreteRules($rules): void
152
    {
153 80
        $parser = new Parser();
154 80
        $lexer = new Lexer();
155
156 80
        foreach ($rules as $rule) {
157 80
            $this->ruleToField(
158 80
                $parser->parse(
159 80
                    $lexer->tokenize($rule),
160 80
                    $this->rules,
161 80
                    $this->alias
162
                )
163
            );
164
        }
165 80
    }
166
167
    /**
168
     * Apply rules to a field.
169
     *
170
     * @param array $rules
171
     */
172 80
    private function ruleToField(array $rules): void
173
    {
174 80
        foreach ($rules as $rule) {
175 80
            $field = $rule[0];
176 80
            $ruleProps = $this->rules[$rule[1]];
177 80
            $ruleParams = $rule[2];
178
179
            //initialize message array
180 80
            $this->messages[$field] = $this->messages[$field] ?? [];
181
182
            //check if value is isset in data
183 80
            if ($this->checkValue($field)) {
184 3
                continue;
185
            }
186
187 78
            $instance = $ruleProps['instance'] ?? new $ruleProps['full_class']();
188
189
            //invoke custom rule validate
190 78
            if ($instance instanceof CustomRule) {
191
                $this->invokeValidate($instance, $field, $ruleParams);
192
                continue;
193
            }
194
195
            //invoke validate section of the filter
196 78
            if ($instance instanceof RuleValidateInterface) {
197 78
                $this->invokeValidate($instance, $field, $ruleParams);
198
            }
199
200
            //invoke sanitize section of the filter
201 78
            if ($instance instanceof RuleSanitizeInterface) {
202 78
                $instance->sanitize($this->sanitizedData[$field]);
203
            }
204
        }
205 80
    }
206
207
    /**
208
     * Check if a field exist in data.
209
     *
210
     * @param string $field
211
     *
212
     * @return bool
213
     */
214 80
    private function checkValue(string &$field): bool
215
    {
216 80
        if (isset($this->data[$field])) {
217 78
            return false;
218
        }
219
220 3
        $this->errors++;
221 3
        $this->messages[$field][] = "Form field '{$field}' missing.";
222
223 3
        return true;
224
    }
225
226
    /**
227
     * Invoke validate.
228
     *
229
     * @param RuleValidateInterface $instance
230
     * @param string                $field
231
     * @param array                 $ruleParams
232
     */
233 78
    private function invokeValidate(RuleValidateInterface &$instance, string $field, array $ruleParams): void
234
    {
235 78
        array_unshift($ruleParams, $this->data[$field]);
236
237 78
        if (call_user_func_array(array($instance, 'validate'), $ruleParams)) {
238 46
            $this->errors++;
239
240 46
            $message = $instance->getMessage();
241
242 46
            if (strlen($message)) {
243 46
                $this->messages[$field][] = $message;
244
            }
245
        }
246 78
    }
247
}
248