Passed
Pull Request — master (#552)
by Théo
02:09
created

RegexChecker::isRegexLike()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
nc 5
nop 1
dl 0
loc 27
rs 9.2222
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Humbug\PhpScoper;
6
7
use function array_pop;
8
use function count;
9
use function explode;
10
use function in_array;
11
use function preg_last_error;
12
use function preg_last_error_msg;
13
use function preg_match as native_preg_match;
14
use function Safe\sprintf;
15
use function str_split;
16
use function strlen;
17
18
// TODO: move this under a Configuration namespace
19
final class RegexChecker
20
{
21
    // https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
22
    private const PATTERN_MODIFIERS = [
23
        'i',
24
        'm',
25
        's',
26
        'x',
27
        'A',
28
        'D',
29
        'S',
30
        'U',
31
        'X',
32
        'J',
33
        'u',
34
    ];
35
36
    public function isRegexLike(string $value): bool
37
    {
38
        $valueLength = strlen($value);
39
40
        if ($valueLength < 2) {
41
            return false;
42
        }
43
44
        $firstCharacter = $value[0];
45
46
        if (!self::isValidDelimiter($firstCharacter)) {
47
            return false;
48
        }
49
50
        $parts = explode($firstCharacter, $value);
51
52
        if (false === $parts || count($parts) !== 3) {
53
            return false;
54
        }
55
56
        $lastPart = array_pop($parts);
57
58
        if (!self::isValidRegexFlags($lastPart)) {
59
            return false;
60
        }
61
62
        return true;
63
    }
64
65
    public function validateRegex(string $regex): ?string
66
    {
67
        if (@native_preg_match($regex, '') !== false) {
68
            return null;
69
        }
70
71
        return sprintf(
72
            'Invalid regex: %s (code %s)',
73
            preg_last_error_msg(),
74
            preg_last_error(),
75
        );
76
    }
77
78
    private static function isValidDelimiter(string $delimiter): bool
79
    {
80
        // This is not ideal as not true but is good enough for our case.
81
        // See https://github.com/humbug/php-scoper/issues/597
82
        return '\\' !== $delimiter && native_preg_match('/^\p{L}$/u', $delimiter) === 0;
83
    }
84
85
    private static function isValidRegexFlags(string $value): bool
86
    {
87
        if ('' === $value) {
88
            return true;
89
        }
90
91
        $characters = str_split($value);
92
93
        foreach ($characters as $character) {
94
            if (!in_array($character, self::PATTERN_MODIFIERS, true)) {
95
                return false;
96
            }
97
        }
98
99
        return true;
100
    }
101
}
102