Completed
Push — b0.3.0 ( 6a5845...eb8c06 )
by Sebastian
02:41
created

Filter.php$0 ➔ ruleToField()   A

Complexity

Conditions 5

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 26
ccs 13
cts 13
cp 1
crap 5
rs 9.1928
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 82
    public function __construct()
59
    {
60 82
        [$this->rules, $this->alias] = RuleBuilder::build();
61 82
    }
62
63
    /**
64
     * Add custom rules to filter.
65
     *
66
     * @param array $customRules
67
     */
68 1
    public function addCustomRules(array $customRules): void
69
    {
70 1
        [$rules, $alias] = RuleBuilder::buildCustom($customRules);
71
72 1
        $this->rules = array_merge($this->rules, $rules);
73 1
        $this->alias = array_merge($this->alias, $alias);
74 1
    }
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 82
    public function filter($data, $rule): Result
89
    {
90 82
        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 27
        if (is_string($rule)) {
98 26
            $this->sanitizedData = $this->data = ['data' => $data];
99 26
            $this->interpreteRules(['data '.$rule]);
100
101 26
            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 54
    public function getErrors(): int
124
    {
125 54
        return $this->errors;
126
    }
127
128
    /**
129
     * Return error messages.
130
     *
131
     * @return array
132
     */
133 27
    public function getMessages(): array
134
    {
135 27
        return $this->messages;
136
    }
137
138
    /**
139
     * Return passed data.
140
     *
141
     * @return array
142
     */
143 3
    public function getData(): array
144
    {
145 3
        return $this->sanitizedData;
146
    }
147
148
    /**
149
     * Get parsed rules.
150
     */
151 81
    private function interpreteRules($rules): void
152
    {
153 81
        $parser = new Parser();
154 81
        $lexer = new Lexer();
155
156 81
        foreach ($rules as $rule) {
157 81
            $this->ruleToField(
158 81
                $parser->parse(
159 81
                    $lexer->tokenize($rule),
160 81
                    $this->rules,
161 81
                    $this->alias
162
                )
163
            );
164
        }
165 81
    }
166
167
    /**
168
     * Apply rules to a field.
169
     *
170
     * @param array $rules
171
     */
172 81
    private function ruleToField(array $rules): void
173
    {
174 81
        foreach ($rules as $rule) {
175 81
            $field = $rule[0];
176 81
            $ruleProps = $this->rules[$rule[1]];
177 81
            $ruleParams = $rule[2];
178
179
            //initialize message array
180 81
            $this->messages[$field] = $this->messages[$field] ?? [];
181
182
            //check if value is isset in data
183 81
            if ($this->checkValue($field)) {
184 3
                continue;
185
            }
186
187 79
            $instance = $ruleProps['instance'] ?? new $ruleProps['full_class']();
188
189
            //invoke validate section of the filter or
190
            //invoke custom rule validate
191 79
            if ($instance instanceof RuleValidateInterface) {
192 79
                $this->invokeValidate($instance, $field, $ruleParams);
193
            }
194
195
            //invoke sanitize section of the filter
196 79
            if ($instance instanceof RuleSanitizeInterface) {
197 79
                $instance->sanitize($this->sanitizedData[$field]);
198
            }
199
        }
200 81
    }
201
202
    /**
203
     * Check if a field exist in data.
204
     *
205
     * @param string $field
206
     *
207
     * @return bool
208
     */
209 81
    private function checkValue(string &$field): bool
210
    {
211 81
        if (isset($this->data[$field])) {
212 79
            return false;
213
        }
214
215 3
        $this->errors++;
216 3
        $this->messages[$field][] = "Form field '{$field}' missing.";
217
218 3
        return true;
219
    }
220
221
    /**
222
     * Invoke validate.
223
     *
224
     * @param RuleValidateInterface $instance
225
     * @param string                $field
226
     * @param array                 $ruleParams
227
     */
228 79
    private function invokeValidate(RuleValidateInterface &$instance, string $field, array $ruleParams): void
229
    {
230 79
        array_unshift($ruleParams, $this->data[$field]);
231
232 79
        if (call_user_func_array(array($instance, 'validate'), $ruleParams)) {
233 46
            $this->errors++;
234
235 46
            $message = $instance->getMessage();
236
237 46
            if (strlen($message)) {
238 46
                $this->messages[$field][] = $message;
239
            }
240
        }
241 79
    }
242
}
243