Completed
Branch master (d39ff3)
by Pierre-Henry
32:32
created

Validate::hex()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 7
rs 9.4285
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @title            Validate Class
4
 * @desc             Various methods to Validate.
5
 *
6
 * @author           Pierre-Henry Soria <[email protected]>
7
 * @copyright        (c) 2012-2018, Pierre-Henry Soria. All Rights Reserved.
8
 * @license          GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
9
 * @package          PH7 / Framework / Security / Validate
10
 */
11
12
namespace PH7\Framework\Security\Validate;
13
14
defined('PH7') or exit('Restricted access');
15
16
use DateTime;
17
use Exception;
18
use PH7\DbTableName;
19
use PH7\ExistsCoreModel;
20
use PH7\Framework\Config\Config;
21
use PH7\Framework\Error\CException\PH7InvalidArgumentException;
22
use PH7\Framework\Math\Measure\Year as YearMeasure;
23
use PH7\Framework\Security\Ban\Ban;
24
use PH7\Framework\Str\Str;
25
26
class Validate
27
{
28
    const REGEX_NOT_NAME_PATTERN = '`(?:[\|<>"\=\]\[\}\{\\\\$£€@%~^#\(\):;\?!¿¡\*])|(?:(?:https?|ftps?)://)|(?:[0-9])`';
29
    const REGEX_DATE_FORMAT = '`^\d\d/\d\d/\d\d\d\d$`';
30
31
    const MAX_INT_NUMBER = 999999999999;
32
33
    const MIN_NAME_LENGTH = 2;
34
    const MAX_NAME_LENGTH = 20;
35
36
    const HEX_HASH = '#';
37
    const MIN_HEX_LENGTH = 3;
38
    const MAX_HEX_LENGTH = 6;
39
40
    const DEF_MIN_USERNAME_LENGTH = 3;
41
    const DEF_MIN_PASS_LENGTH = 6;
42
    const DEF_MAX_PASS_LENGTH = 60;
43
    const DEF_MIN_AGE = 18;
44
    const DEF_MAX_AGE = 99;
45
46
    /** @var Str */
47
    private $oStr;
48
49
    public function __construct()
50
    {
51
        $this->oStr = new Str;
52
    }
53
54
    /**
55
     * Check the type of a value.
56
     *
57
     * @param string $sValue
58
     * @param string $sType Type whose value should be (case-insensitive).
59
     * @param bool $bRequired Default TRUE
60
     *
61
     * @return bool
62
     *
63
     * @throws PH7InvalidArgumentException If the type doesn't exist.
64
     */
65
    public static function type($sValue, $sType, $bRequired = true)
66
    {
67
        $sType = strtolower($sType); // Case-insensitive type.
68
69
        if (false === $bRequired && 0 === (new Str)->length($sValue)) // Yoda Condition ;-)
70
            return true;
71
72
        switch ($sType) {
73
            case 'str':
74
            case 'string':
75
                $bValid = is_string($sValue);
76
                break;
77
78
            case 'int':
79
            case 'integer':
80
                $bValid = is_int($sValue);
81
                break;
82
83
            case 'float':
84
            case 'double':
85
                $bValid = is_float($sValue);
86
                break;
87
88
            case 'bool':
89
            case 'boolean':
90
                $bValid = is_bool($sValue);
91
                break;
92
93
            case 'num':
94
            case 'numeric':
95
                $bValid = is_numeric($sValue);
96
                break;
97
98
            case 'arr':
99
            case 'array':
100
                $bValid = is_array($sValue);
101
                break;
102
103
            case 'null':
104
                $bValid = is_null($sValue);
105
                break;
106
107
            case 'obj':
108
            case 'object':
109
                $bValid = is_object($sValue);
110
                break;
111
112
            default:
113
                throw new PH7InvalidArgumentException('Invalid Type!');
114
        }
115
116
        return $bValid;
117
    }
118
119
    /**
120
     * Validate Is String.
121
     *
122
     * @param $sValue
123
     * @param int $iMin Default NULL
124
     * @param int $iMax Default NULL
125
     *
126
     * @return bool
127
     */
128
    public function str($sValue, $iMin = null, $iMax = null)
129
    {
130
        $sValue = filter_var($sValue, FILTER_SANITIZE_STRING);
131
132
        if (!empty($sValue)) {
133
            if (!empty($iMin) && $this->oStr->length($sValue) < $iMin)
134
                return false;
135
            elseif (!empty($iMax) && $this->oStr->length($sValue) > $iMax)
136
                return false;
137
            elseif (!is_string($sValue))
138
                return false;
139
            else
140
                return true;
141
        }
142
        return false;
143
    }
144
145
    /**
146
     * Validate if it's an integer.
147
     *
148
     * @param int $iInt
149
     * @param int $iMin Default 0
150
     * @param int $iMax Default 999999999999
151
     *
152
     * @return bool
153
     */
154
    public function int($iInt, $iMin = 0, $iMax = self::MAX_INT_NUMBER)
155
    {
156
        $iInt = filter_var($iInt, FILTER_SANITIZE_NUMBER_INT);
157
158
        return filter_var($iInt, FILTER_VALIDATE_INT, static::getFilterOption($iMin, $iMax)) !== false;
159
160
    }
161
162
    /**
163
     * Validate if it's a numeric.
164
     *
165
     * @param string|int (numeric string or integer) $mNumeric
166
     *
167
     * @return bool
168
     */
169
    public function numeric($mNumeric)
170
    {
171
        return is_numeric($mNumeric);
172
    }
173
174
    /**
175
     * Validate if it's a digit character.
176
     *
177
     * @param string (numeric string) $sDigit
178
     *
179
     * @return bool
180
     */
181
    public function digitChar($sDigit)
182
    {
183
        return ctype_digit($sDigit);
184
    }
185
186
    /**
187
     * Validate if it's a float type.
188
     *
189
     * @param float $fFloat
190
     * @param float|int $mMin Default 0
191
     * @param float|int $mMax Default 999999999999
192
     *
193
     * @return bool
194
     */
195
    public function float($fFloat, $mMin = 0, $mMax = self::MAX_INT_NUMBER)
196
    {
197
        $fFloat = filter_var($fFloat, FILTER_SANITIZE_NUMBER_FLOAT);
198
199
        return filter_var($fFloat, FILTER_VALIDATE_FLOAT, static::getFilterOption($mMin, $mMax)) !== false;
200
    }
201
202
    /**
203
     * Validate if it's a boolean type.
204
     *
205
     * @param bool $bBool
206
     *
207
     * @return bool
208
     */
209
    public function bool($bBool)
210
    {
211
        return filter_var($bBool, FILTER_VALIDATE_BOOLEAN) !== false;
212
    }
213
214
    /**
215
     * Validate Username.
216
     *
217
     * @param string $sUsername
218
     * @param int $iMin Default 3
219
     * @param int $iMax Default 40
220
     * @param string $sTable Default DbTableName::MEMBER
221
     *
222
     * @return bool
223
     */
224
    public function username($sUsername, $iMin = self::DEF_MIN_USERNAME_LENGTH, $iMax = PH7_MAX_USERNAME_LENGTH, $sTable = DbTableName::MEMBER)
225
    {
226
        $sUsername = trim($sUsername);
227
228
        return (
229
            preg_match('#^' . PH7_USERNAME_PATTERN . '{' . $iMin . ',' . $iMax . '}$#', $sUsername) &&
230
            !is_file(PH7_PATH_ROOT . $sUsername . PH7_PAGE_EXT) && !Ban::isUsername($sUsername) &&
231
            !(new ExistsCoreModel)->username($sUsername, $sTable)
232
        );
233
    }
234
235
    /**
236
     * Validate Password.
237
     *
238
     * @param string $sPwd
239
     * @param int $iMin Default 6
240
     * @param int $iMax Default 60
241
     *
242
     * @return bool
243
     */
244
    public function password($sPwd, $iMin = self::DEF_MIN_PASS_LENGTH, $iMax = self::DEF_MAX_PASS_LENGTH)
245
    {
246
        $iPwdLength = $this->oStr->length($sPwd);
247
248
        return $iPwdLength >= $iMin && $iPwdLength <= $iMax;
249
    }
250
251
    /**
252
     * Validate Email.
253
     *
254
     * @param string $sEmail
255
     * @param bool $bRealHost Checks whether the Email Host is valid.
256
     *
257
     * @return bool
258
     */
259
    public function email($sEmail, $bRealHost = false)
260
    {
261
        $sEmail = filter_var($sEmail, FILTER_SANITIZE_EMAIL);
262
263
        if ($bRealHost) {
264
            $sEmailHost = substr(strrchr($sEmail, '@'), 1);
265
            // This function now works with Windows since version PHP 5.3, so we mustn't include the PEAR NET_DNS library.
266
            if (!(checkdnsrr($sEmailHost, 'MX') && checkdnsrr($sEmailHost, 'A'))) {
267
                return false;
268
            }
269
        }
270
271
        return filter_var($sEmail, FILTER_VALIDATE_EMAIL) !== false &&
272
            $this->oStr->length($sEmail) <= PH7_MAX_EMAIL_LENGTH && !Ban::isEmail($sEmail);
273
    }
274
275
    /**
276
     * Validate Birthday.
277
     *
278
     * @param string $sValue The date format must be formatted like this: mm/dd/yyyy
279
     * @param int $iMin Default 18
280
     * @param int $iMax Default 99
281
     *
282
     * @return bool
283
     */
284
    public function birthDate($sValue, $iMin = self::DEF_MIN_AGE, $iMax = self::DEF_MAX_AGE)
285
    {
286
        if (empty($sValue) || !preg_match(static::REGEX_DATE_FORMAT, $sValue)) {
287
            return false;
288
        }
289
290
        $aBirthDate = explode('/', $sValue); // Format is "mm/dd/yyyy"
291
        if (!checkdate($aBirthDate[0], $aBirthDate[1], $aBirthDate[2])) {
292
            return false;
293
        }
294
295
        $iUserAge = (new YearMeasure($aBirthDate[2], $aBirthDate[0], $aBirthDate[1]))->get(); // Get the current user's age
296
297
        return $iUserAge >= $iMin && $iUserAge <= $iMax;
298
    }
299
300
    /**
301
     * Validate Date.
302
     *
303
     * @param string $sValue
304
     *
305
     * @return bool
306
     */
307
    public function date($sValue)
308
    {
309
        try {
310
            new DateTime($sValue);
311
            return true;
312
        } catch (Exception $oE) {
313
            return false;
314
        }
315
    }
316
317
    /**
318
     * Validate URL.
319
     *
320
     * @param string $sUrl
321
     * @param bool $bRealUrl Checks if the current URL exists.
322
     *
323
     * @return bool
324
     */
325
    public function url($sUrl, $bRealUrl = false)
326
    {
327
        $sUrl = filter_var($sUrl, FILTER_SANITIZE_URL);
328
329
        if (filter_var($sUrl, FILTER_VALIDATE_URL) === false || $this->oStr->length($sUrl) >= PH7_MAX_URL_LENGTH) {
330
            return false;
331
        }
332
333
        if ($bRealUrl) {
334
            /**
335
             * Checks if the URL is valid and contains the HTTP status code '200 OK', '301 Moved Permanently' or '302 Found'
336
             */
337
            $rCurl = curl_init();
338
            curl_setopt_array($rCurl, [CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $sUrl]);
339
            curl_exec($rCurl);
340
            $iResponse = (int)curl_getinfo($rCurl, CURLINFO_HTTP_CODE);
341
            curl_close($rCurl);
342
343
            return $iResponse === 200 || $iResponse === 301 || $iResponse === 302;
344
        }
345
346
        return true;
347
    }
348
349
    /**
350
     * Validate IP address.
351
     *
352
     * @param string $sIp
353
     *
354
     * @return bool
355
     */
356
    public function ip($sIp)
357
    {
358
        return filter_var($sIp, FILTER_VALIDATE_IP) !== false;
359
    }
360
361
    /**
362
     * Validate international phone numbers in EPP format.
363
     *
364
     * @param string $sNumber
365
     *
366
     * @return bool
367
     */
368
    public function phone($sNumber)
369
    {
370
        return preg_match('#^' . Config::getInstance()->values['validate']['phone.pattern'] . '$#', $sNumber);
371
    }
372
373
    /**
374
     * @param string $sHexCode
375
     *
376
     * @return bool
377
     */
378
    public function hex($sHexCode)
379
    {
380
        $sHexChars = str_replace(self::HEX_HASH, '', $sHexCode);
381
        $iLength = strlen($sHexChars);
382
383
        return strpos($sHexCode, '#') !== false && $iLength >= self::MIN_HEX_LENGTH && $iLength <= self::MAX_HEX_LENGTH;
384
    }
385
386
    /**
387
     * Validate Name.
388
     *
389
     * @param string $sName
390
     * @param int $iMin Default 2
391
     * @param int $iMax Default 20
392
     *
393
     * @return bool
394
     */
395
    public function name($sName, $iMin = self::MIN_NAME_LENGTH, $iMax = self::MAX_NAME_LENGTH)
396
    {
397
        // Check the length
398
        if ($this->oStr->length($sName) < $iMin || $this->oStr->length($sName) > $iMax) {
399
            return false;
400
        }
401
402
        // Check the name pattern. Name cannot contain any of the below characters
403
        if (preg_match(static::REGEX_NOT_NAME_PATTERN, $sName)) {
404
            return false;
405
        }
406
407
        return true;
408
    }
409
410
    /*
411
    /**
412
     * Check Email with test for check if the host email is valid.
413
     *
414
     * @param string $sEmail
415
     *
416
     * @return bool
417
     */
418
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
419
    public function emailHost($sEmail)
420
    {
421
        // The email address must be properly formatted
422
        if (!$this->email($sEmail))
423
            return false;
424
425
        // It gets domain
426
        list(, $sDomain ) = explode('@', $sEmail);
427
        // We look for MX records in DNS
428
        if (getmxrr($sDomain, $aMxHost))
429
            $sConnectAddress = $aMxHost[0];
430
        else
431
            $sConnectAddress = $sDomain;
432
        // We created the connection on SMTP port (25)
433
        if ($rConnect = @fsockopen($sConnectAddress, 25, $iErrno, $sErrStr))
434
        {
435
            if (preg_match("/^220/", $sOut = fgets($rConnect, 1024)))
436
            {
437
                fputs($rConnect, "HELO {$_SERVER['HTTP_HOST']}\r\n");
438
                $sOut = fgets($rConnect, 1024);
439
                fputs($rConnect, "MAIL FROM: <{$sEmail}>\r\n");
440
                $sFrom = fgets($rConnect, 1024);
441
                fputs($rConnect, "RCPT TO: <{$sEmail}>\r\n");
442
                $sTo = fgets($rConnect, 1024);
443
                fputs($rConnect, "QUIT\r\n");
444
                fclose($rConnect);
445
                // If the code returned by the RCPT TO is 250 or 251 (cf: RFC)
446
                // Then the address exists
447
                if (!preg_match("/^250/", $sTo) && !preg_match("/^251/", $sTo))
448
                // Address rejected by the serve
449
                    return false;
450
                else
451
                // Accepted by the server address
452
                    return true;
453
            }
454
            else
455
            {
456
                // The server did not respond
457
                return false;
458
            }
459
        }
460
        else
461
        {
462
            // You can display an error message by uncommenting the following two lines or leave the return value of the false boolean.
463
            // echo "Cannot connect to the mail server\n";
464
            // echo "$iErrno - $sErrStr\n";
465
            return false;
466
        }
467
    }
468
    //*/
469
470
    /**
471
     * Get option for some filter_var().
472
     *
473
     * @param float|int $mMin Minimum range.
474
     * @param float|int $mMax Maximum range.
475
     *
476
     * @return array
477
     */
478
    protected static function getFilterOption($mMin, $mMax)
479
    {
480
        return ['options' => ['min_range' => $mMin, 'max_range' => $mMax]];
481
    }
482
483
}
484