Sanitize.php 8.35 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/2/16
 * Time: 10:57 PM
 */

namespace qfq;

Carsten  Rose's avatar
Carsten Rose committed
11
use qfq;
12
13
14

//use qfq\CodeException;
//use qfq\UserFormException;
15

16
require_once(__DIR__ . '/../../qfq/Constants.php');
17
//require_once(__DIR__ . '/../exceptions/UserFormException.php');
18

Carsten  Rose's avatar
Carsten Rose committed
19
/**
Carsten  Rose's avatar
Carsten Rose committed
20
 * Class Sanitize
Carsten  Rose's avatar
Carsten Rose committed
21
22
 * @package qfq
 */
Carsten  Rose's avatar
Carsten Rose committed
23
class Sanitize {
24

25

26
27
28
29
30
    private function __construct() {
        // Class should never be instantiated
    }

    /**
31
32
     * Check $value against given checkType/pattern. If check succeed, returns values.
     *   If check fails, depending on $mode, throws an UserException or return an empty string.
33
     *
34
     * @param string $value value to check
35
36
     * @param string $sanitizeClass
     * @param string $pattern Pattern as regexp
37
     * @param array $decimalFormat with [ size, precision ]
Carsten  Rose's avatar
Carsten Rose committed
38
     * @param string $mode SANITIZE_EXCEPTION | SANITIZE_EMPTY_STRING
Carsten  Rose's avatar
Carsten Rose committed
39
     *
40
     * @return string
41
     * @throws UserFormException
42
     * @throws \qfq\CodeException
43
     */
44
    public static function sanitize($value, $sanitizeClass = SANITIZE_DEFAULT, $pattern = '', $decimalFormat = null, $mode = SANITIZE_EMPTY_STRING) {
Elias Villiger's avatar
Elias Villiger committed
45
        // Prepare pattern check
46
        switch ($sanitizeClass) {
47
            case SANITIZE_ALLOW_PATTERN:
48
49
                break;

50
            case SANITIZE_ALLOW_DIGIT:
51
            case SANITIZE_ALLOW_NUMERICAL:
52
53
            case SANITIZE_ALLOW_EMAIL:
            case SANITIZE_ALLOW_ALNUMX:
54
            case SANITIZE_ALLOW_ALLBUT:
55
                $arr = self::inputCheckPatternArray();
56
                $pattern = $arr[$sanitizeClass];
57
58
                break;

Elias Villiger's avatar
Elias Villiger committed
59
            case SANITIZE_ALLOW_ALL: // no checkType specified.
60
                return $value;
61

62
            default:
63
                throw new CodeException("Unknown checkType: " . $sanitizeClass, ERROR_UNKNOWN_CHECKTYPE);
64
65
        }

66
67
68
69
        // decimalFormat
        if ($decimalFormat !== null) {
            if ($sanitizeClass !== SANITIZE_ALLOW_PATTERN && $sanitizeClass !== SANITIZE_ALLOW_DIGIT) {
                // overwrite pattern
70
                $pattern = self::getDecimalFormatPattern($decimalFormat);
71
72
73
            }
        }

Elias Villiger's avatar
Elias Villiger committed
74
        // Pattern check
Carsten  Rose's avatar
Carsten Rose committed
75
        if ($pattern === '' || preg_match("/$pattern/", $value) === 1) {
76
            return $value;
Carsten  Rose's avatar
Carsten Rose committed
77
        }
78
79
80

        // check failed
        if ($mode === SANITIZE_EXCEPTION) {
Elias Villiger's avatar
Elias Villiger committed
81
82
            $errorCode = ERROR_PATTERN_VIOLATION;
            $errorText = "Value '$value' violates checkrule " . $sanitizeClass . " with pattern '$pattern'.";
83
            throw new UserFormException($errorText, $errorCode);
84
85
        }

86
        return SANITIZE_VIOLATE . $sanitizeClass . SANITIZE_VIOLATE;
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    }

    /**
     * Check $value against $formElement's min/max values. If check succeeds, returns value.
     *   If check fails, depending on $mode, throws an UserException or return an empty string.
     *
     * @param string $value value to check
     * @param $formElement
     * @param string $mode SANITIZE_EXCEPTION | SANITIZE_EMPTY_STRING
     *
     * @return string
     * @throws UserFormException
     * @throws \qfq\CodeException
     */
    public static function checkMinMax($value, $formElement, $mode = SANITIZE_EMPTY_STRING) {
102
103
        $min = Support::setIfNotSet($formElement, FE_MIN);
        $max = Support::setIfNotSet($formElement, FE_MAX);
104
105
106
        $errorCode = 0;
        $errorText = '';

107
        if ($min !== '' && $value < $min) {
108
109
110
            $errorCode = ERROR_SMALLER_THAN_MIN;
            $errorText = "Value '$value' is smaller than the allowed minimum of '$min'.";
        }
111
        if ($max !== '' && $value > $max) {
112
113
            $errorCode = ERROR_LARGER_THAN_MAX;
            $errorText = "Value '$value' is larger than the allowed maximum of '$max'.";
114
115
        }

116
        if ($errorCode == 0)
Elias Villiger's avatar
Elias Villiger committed
117
118
            return $value;

119
120
121
        // check failed
        if ($mode === SANITIZE_EXCEPTION) {
            throw new UserFormException($errorText, $errorCode);
Elias Villiger's avatar
Elias Villiger committed
122
        }
123
124

        return '';
125
126
    }

127
128
129
130
131
132
133
134
135
136
137
    /**
     * Returns the regexp pattern to match a decimal number with the format in $decimalFormat.
     *
     * @param array $decimalFormat with [ size, precision ]
     *
     * @return string
     */
    public static function getDecimalFormatPattern($decimalFormat) {
        return "^[0-9]{0,$decimalFormat[0]}(\.[0-9]{0,$decimalFormat[1]})?$";
    }

138
139
140
    /**
     * @return array
     */
141
    public static function inputCheckPatternArray() {
142
        //EMail Regex: http://www.regular-expressions.info/email.html
143
        return [
Carsten  Rose's avatar
Carsten Rose committed
144
145
            SANITIZE_ALLOW_ALNUMX => '^[@\-_\.,;: \/\(\)a-zA-Z0-9ÀÈÌÒÙàèìòùÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÃÑÕãñõÄËÏÖÜŸäëïöüÿç]*$', // ':alnum:' does not work here in FF
            SANITIZE_ALLOW_DIGIT => '^[\d]*$',
146
            SANITIZE_ALLOW_NUMERICAL => '^[\d.+-]*$',
Carsten  Rose's avatar
Carsten Rose committed
147
            SANITIZE_ALLOW_EMAIL => '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
148
            SANITIZE_ALLOW_PATTERN => '',
Carsten  Rose's avatar
Carsten Rose committed
149
150
            SANITIZE_ALLOW_ALLBUT => '^[^\[\]{}%&\\\\#]*$',
            SANITIZE_ALLOW_ALL => '.*',
151
        ];
152
153
    }

154
155
156
157
    /**
     * Sanatizes a filename. Copied from http://www.phpit.net/code/filename-safe/
     *
     * @param $filename
Carsten  Rose's avatar
Carsten Rose committed
158
     *
159
160
     * @return mixed
     */
161
    public static function safeFilename($filename, $flagBaseName = false) {
162
163
164
165
166
167
168
        $search = array(
            // Definition of German Umlauts START
            '/ß/',
            '/ä/', '/Ä/',
            '/ö/', '/Ö/',
            '/ü/', '/Ü/',
            // Definition of German Umlauts ENDE
169
            '([^[:alnum:]._])' // Disallow 'none alphanumeric'. Allow dot or underscore.
170
171
172
173
174
175
176
        );

        $replace = array(
            'ss',
            'ae', 'Ae',
            'oe', 'Oe',
            'ue', 'Ue',
Carsten  Rose's avatar
Carsten Rose committed
177
            '_',
178
179
        );

180
181
182
183
        if ($flagBaseName) {
            $filename = basename($filename);
        }

184
185
186
        return preg_replace($search, $replace, $filename);
    } // safeFilename()

187
188
189
190
    /**
     * htmlentities($data) - if $data is an array, convert it recursively.
     *
     * @param string|array $data
Carsten  Rose's avatar
Carsten Rose committed
191
     * @param int $mode
Carsten  Rose's avatar
Carsten Rose committed
192
     *
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
     * @return array|string
     */
    public static function htmlentitiesArr($data, $mode = ENT_QUOTES) {

        if (is_string($data)) {
            htmlentities($data, $mode);
        }

        if (is_array($data)) {
            foreach ($data as $key => $value) {
                $data[$key] = self::htmlentitiesArr($value, $mode);
            }
        }

        return $data;
    }

210
    /**
Carsten  Rose's avatar
Carsten Rose committed
211
212
213
     * Take the given $item (or iterates over all elements of the given array) and normalize the content.
     * Only strings will be normalized. Sub arrays will be recursived normalized. Numeric content is skipped.
     * Throws an exception for unknown content.
214
215
     *
     * It's important to normalize the user input: e.g. someone is searching for a record and input the search string
Carsten  Rose's avatar
Carsten Rose committed
216
     * with composed characters (happens e.g. on Apple Mac / Safari without special user invention).
217
     *
218
     * @param array|string $item
Carsten  Rose's avatar
Carsten Rose committed
219
     *
220
221
     * @return array|string
     * @throws CodeException
222
     */
223
224
225
226
227
228
229
230
231
232
233
    public static function normalize($item) {

        if (is_array($item)) {
            foreach ($item as $key => $value) {
                $value = self::normalize($value);
                $item[$key] = $value;
            }
        } else {
            if (is_string($item)) {
                $item = \normalizer::normalize($item, \Normalizer::FORM_C);
            } elseif (!is_numeric($item)) {
234
235
236
                throw new qfq\CodeException ("Expect type 'string / numeric / array' - but there is something else.", ERROR_UNEXPECTED_TYPE);
            }
        }
Carsten  Rose's avatar
Carsten Rose committed
237

238
        return $item;
239
    }
240
241

    /**
Carsten  Rose's avatar
Carsten Rose committed
242
243
     * urlencode() any input and decode again. This normalizes all characters and guarantees that there are no more
     * urlencoded characters.
244
245
     *
     * @param array|string $item
Carsten  Rose's avatar
Carsten Rose committed
246
     *
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
     * @return array|string
     * @throws CodeException
     */
    public static function urlDecodeArr($item) {

        if (is_array($item)) {
            foreach ($item as $key => $value) {
                $value = self::urlDecodeArr($value);
                $item[$key] = $value;
            }
        } else {
            if (is_string($item)) {
                $item = urldecode($item);
            } elseif (!is_numeric($item)) {
                throw new qfq\CodeException ("Expect type 'string / numeric / array' - but there is something else.", ERROR_UNEXPECTED_TYPE);
            }
        }
Carsten  Rose's avatar
Carsten Rose committed
264

265
266
267
        return $item;

    }
268
}