Passed
Push — master ( 5fbab0...5feb54 )
by Paul
08:33
created

Sanitizer   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 315
Duplicated Lines 0 %

Test Coverage

Coverage 81.89%

Importance

Changes 12
Bugs 5 Features 5
Metric Value
wmc 41
eloc 94
c 12
b 5
f 5
dl 0
loc 315
ccs 86
cts 105
cp 0.8189
rs 9.1199

25 Methods

Rating   Name   Duplication   Size   Complexity  
A sanitizeArray() 0 3 1
A sanitizeArrayInt() 0 3 1
A run() 0 9 3
A __construct() 0 4 1
A sanitizePostIds() 0 5 1
A sanitizeTermIds() 0 5 1
A sanitizeUrl() 0 11 4
A sanitizeUserName() 0 8 3
A sanitizeName() 0 4 1
A sanitizeTextPost() 0 3 1
A sanitizeSlug() 0 3 1
A buildSanitizers() 0 9 3
A sanitizeText() 0 3 1
A sanitizeTextMultiline() 0 3 1
A sanitizeTextHtml() 0 10 1
A sanitizeArrayString() 0 7 1
A sanitizeBool() 0 3 1
A sanitizeKey() 0 3 1
A sanitizeUserEmail() 0 8 3
A sanitizeEmail() 0 3 1
A sanitizeJson() 0 13 4
A sanitizeId() 0 8 2
A sanitizeUserIds() 0 5 1
A sanitizeDate() 0 7 2
A sanitizeInt() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Sanitizer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Sanitizer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use GeminiLabs\SiteReviews\Helper;
6
use GeminiLabs\SiteReviews\Helpers\Arr;
7
use GeminiLabs\SiteReviews\Helpers\Cast;
8
use GeminiLabs\SiteReviews\Helpers\Str;
9
use GeminiLabs\SiteReviews\Modules\Multilingual;
10
11
class Sanitizer
12
{
13
    const JSON_ERROR_CODES = [
14
        JSON_ERROR_DEPTH => 'JSON Error: The maximum stack depth has been exceeded',
15
        JSON_ERROR_STATE_MISMATCH => 'JSON Error: Invalid or malformed JSON',
16
        JSON_ERROR_CTRL_CHAR => 'JSON Error: Control character error, possibly incorrectly encoded',
17
        JSON_ERROR_SYNTAX => 'JSON Error: Syntax error',
18
        JSON_ERROR_UTF8 => 'JSON Error: Malformed UTF-8 characters, possibly incorrectly encoded',
19
        JSON_ERROR_RECURSION => 'JSON Error: One or more recursive references in the value to be encoded',
20
        JSON_ERROR_INF_OR_NAN => 'JSON Error: One or more NAN or INF values in the value to be encoded',
21
        JSON_ERROR_UNSUPPORTED_TYPE => 'JSON Error: A value of a type that cannot be encoded was given',
22
        JSON_ERROR_INVALID_PROPERTY_NAME => 'JSON Error: A property name that cannot be encoded was given',
23
        JSON_ERROR_UTF16 => 'JSON Error: Malformed UTF-16 characters, possibly incorrectly encoded',
24
    ];
25
26
    /**
27
     * @var array
28
     */
29
    public $sanitizers;
30
31
    /**
32
     * @var array
33
     */
34
    public $values;
35
36 38
    public function __construct(array $values = [], array $sanitizers = [])
37
    {
38 38
        $this->sanitizers = $this->buildSanitizers(Arr::consolidate($sanitizers));
39 38
        $this->values = Arr::consolidate($values);
40 38
    }
41
42
    /**
43
     * @return array|bool|string
44
     */
45 38
    public function run()
46
    {
47 38
        $result = $this->values;
48 38
        foreach ($this->values as $key => $value) {
49 38
            if (array_key_exists($key, $this->sanitizers)) {
50 38
                $result[$key] = call_user_func([$this, $this->sanitizers[$key]], $value);
51
            }
52
        }
53 38
        return $result;
54
    }
55
56
    /**
57
     * @param mixed $value
58
     * @return array
59
     */
60 17
    public function sanitizeArray($value)
61
    {
62 17
        return Arr::consolidate($value);
63
    }
64
65
    /**
66
     * @param mixed $value
67
     * @return int[]
68
     */
69 25
    public function sanitizeArrayInt($value)
70
    {
71 25
        return Arr::uniqueInt(Cast::toArray($value));
72
    }
73
74
    /**
75
     * @param mixed $value
76
     * @return string[]
77
     */
78 1
    public function sanitizeArrayString($value)
79
    {
80 1
        $sanitized = array_filter(Cast::toArray($value), 'is_string');
81
        array_walk($sanitized, function (&$value) {
82 1
            $value = $this->sanitizeText($value);
83 1
        });
84 1
        return $sanitized;
85
    }
86
87
    /**
88
     * @param mixed $value
89
     * @return bool
90
     */
91 16
    public function sanitizeBool($value)
92
    {
93 16
        return Cast::toBool($value);
94
    }
95
96
    /**
97
     * If date is invalid then return an empty string.
98
     * @param mixed $value
99
     * @param string $fallback
100
     * @return string
101
     */
102 24
    public function sanitizeDate($value, $fallback = '')
103
    {
104 24
        $date = strtotime(trim(Cast::toString($value)));
105 24
        if (false !== $date) {
106 15
            return wp_date('Y-m-d H:i:s', $date);
107
        }
108 24
        return $fallback;
109
    }
110
111
    /**
112
     * @param mixed $value
113
     * @return string
114
     */
115 25
    public function sanitizeEmail($value)
116
    {
117 25
        return sanitize_email(trim(Cast::toString($value)));
118
    }
119
120
    /**
121
     * @param mixed $value
122
     * @return string
123
     */
124 7
    public function sanitizeId($value)
125
    {
126 7
        require_once ABSPATH.WPINC.'/pluggable.php';
127 7
        $value = $this->sanitizeSlug($value);
128 7
        if (empty($value)) {
129 7
            $value = glsr()->prefix.substr(wp_hash(serialize($this->values), 'nonce'), -12, 8);
130
        }
131 7
        return $value;
132
    }
133
134
    /**
135
     * @param mixed $value
136
     * @return int
137
     */
138 16
    public function sanitizeInt($value)
139
    {
140 16
        return Cast::toInt($value);
141
    }
142
143
    /**
144
     * @param mixed $value
145
     * @return array
146
     */
147
    public function sanitizeJson($value)
148
    {
149
        $result = '';
150
        if (is_scalar($value) && !Helper::isEmpty($value)) {
151
            $result = trim((string) $value);
152
            $result = htmlspecialchars_decode($result);
153
            $result = json_decode($result, true);
154
            $error = json_last_error();
155
            if (array_key_exists($error, static::JSON_ERROR_CODES)) {
156
                glsr_log()->error(static::JSON_ERROR_CODES[$error])->debug($value);
157
            }
158
        }
159
        return wp_unslash(Arr::consolidate($result));
160
    }
161
162
    /**
163
     * This allows lowercase alphannumeric and underscore characters
164
     * @param mixed $value
165
     * @return string
166
     */
167 23
    public function sanitizeKey($value)
168
    {
169 23
        return Str::snakeCase(sanitize_key($this->sanitizeText($value)));
170
    }
171
172
    /**
173
     * This allows lowercase alpha and underscore characters
174
     * @param mixed $value
175
     * @return string
176
     */
177 22
    public function sanitizeName($value)
178
    {
179 22
        $value = Str::snakeCase($this->sanitizeText($value));
180 22
        return preg_replace('/[^a-z_]/', '', $value);
181
    }
182
183
    /**
184
     * @param mixed $value
185
     * @return int[]
186
     */
187 24
    public function sanitizePostIds($value)
188
    {
189 24
        $postIds = Cast::toArray($value);
190 24
        $postIds = array_map('\GeminiLabs\SiteReviews\Helper::getPostId', $postIds);
191 24
        return Arr::uniqueInt($postIds);
192
    }
193
194
    /**
195
     * @param mixed $value
196
     * @return int[]
197
     */
198 24
    public function sanitizeTermIds($value)
199
    {
200 24
        $termIds = Cast::toArray($value);
201 24
        $termIds = array_map('\GeminiLabs\SiteReviews\Helper::getTermTaxonomyId', $termIds);
202 24
        return Arr::uniqueInt($termIds);
203
    }
204
205
    /**
206
     * @param mixed $value
207
     * @return int[]
208
     */
209 24
    public function sanitizeUserIds($value)
210
    {
211 24
        $userIds = Cast::toArray($value);
212 24
        $userIds = array_map('\GeminiLabs\SiteReviews\Helper::getUserId', $userIds);
213 24
        return Arr::uniqueInt($userIds);
214
    }
215
216
    /**
217
     * @param mixed $value
218
     * @return string
219
     */
220 8
    public function sanitizeSlug($value)
221
    {
222 8
        return sanitize_title($this->sanitizeText($value));
223
    }
224
225
    /**
226
     * @param mixed $value
227
     * @return string
228
     */
229 29
    public function sanitizeText($value)
230
    {
231 29
        return sanitize_text_field(trim(Cast::toString($value)));
232
    }
233
234
    /**
235
     * @param mixed $value
236
     * @return string
237
     */
238
    public function sanitizeTextHtml($value)
239
    {
240
        $allowedHtmlPost = wp_kses_allowed_html('post');
241
        $allowedHtml = [
242
            'a' => glsr_get($allowedHtmlPost, 'a'),
243
            'em' => glsr_get($allowedHtmlPost, 'em'),
244
            'strong' => glsr_get($allowedHtmlPost, 'strong'),
245
        ];
246
        $allowedHtml = glsr()->filterArray('sanitize/allowed-html', $allowedHtml, $this);
247
        return wp_kses(trim(Cast::toString($value)), $allowedHtml);
248
    }
249
250
    /**
251
     * @param mixed $value
252
     * @return string
253
     */
254 16
    public function sanitizeTextMultiline($value)
255
    {
256 16
        return sanitize_textarea_field(trim(Cast::toString($value)));
257
    }
258
259
    /**
260
     * Returns slashed data!
261
     * @param mixed $value
262
     * @return string
263
     */
264
    public function sanitizeTextPost($value)
265
    {
266
        return wp_filter_post_kses(trim(Cast::toString($value)));
267
    }
268
269
    /**
270
     * @param mixed $value
271
     * @return string
272
     */
273 16
    public function sanitizeUrl($value)
274
    {
275 16
        $value = trim(Cast::toString($value));
276 16
        if (!Str::startsWith('http://, https://', $value)) {
277 16
            $value = Str::prefix($value, 'https://');
278
        }
279 16
        $url = esc_url_raw($value);
280 16
        if (mb_strtolower($value) === mb_strtolower($url) && filter_var($url, FILTER_VALIDATE_URL) !== false) {
281 15
            return $url;
282
        }
283 16
        return '';
284
    }
285
286
    /**
287
     * @param mixed $value
288
     * @return string
289
     */
290 15
    public function sanitizeUserEmail($value)
291
    {
292 15
        $user = wp_get_current_user();
293 15
        $value = $this->sanitizeEmail($value);
294 15
        if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
295 2
            return Helper::ifEmpty($value, $user->user_email);
296
        }
297 15
        return $value;
298
    }
299
300
    /**
301
     * @param mixed $value
302
     * @return string
303
     */
304 15
    public function sanitizeUserName($value)
305
    {
306 15
        $user = wp_get_current_user();
307 15
        $value = $this->sanitizeText($value);
308 15
        if ($user->exists() && !glsr()->retrieveAs('bool', 'import', false)) {
309 2
            return Helper::ifEmpty($value, $user->display_name);
310
        }
311 15
        return $value;
312
    }
313
314
    /**
315
     * @return array
316
     */
317 38
    protected function buildSanitizers(array $sanitizers)
318
    {
319 38
        foreach ($sanitizers as $key => &$type) {
320 38
            $method = Helper::buildMethodName($type, 'sanitize');
321 38
            $type = method_exists($this, $method)
322 38
                ? $method
323 38
                : 'sanitizeText';
324
        }
325 38
        return $sanitizers;
326
    }
327
}
328