Passed
Pull Request — master (#93)
by
unknown
03:33
created

RangeParser::parse()   C

Complexity

Conditions 13
Paths 34

Size

Total Lines 36
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 15.6406

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 26
c 1
b 0
f 0
nc 34
nop 1
dl 0
loc 36
ccs 15
cts 20
cp 0.75
crap 15.6406
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use DateInterval;
8
use DateTime;
9
use InvalidArgumentException;
10
use function preg_match;
11
12
final class RangeParser
13
{
14
    private const RANGES = [
15
        Schema::TYPE_INT_4_RANGE,
16
        Schema::TYPE_INT_8_RANGE,
17
        Schema::TYPE_NUM_RANGE,
18
        Schema::TYPE_TS_RANGE,
19
        Schema::TYPE_TS_TZ_RANGE,
20
        Schema::TYPE_DATE_RANGE,
21
    ];
22
23
    private ?string $type = null;
24
25 9
    public function __construct(?string $type = null)
26
    {
27 9
        if ($type !== null) {
28 9
            if (self::isAllowedType($type)) {
29 9
                $this->type = $type;
30
            } else {
31
                throw new InvalidArgumentException('Unsupported range type "' . $type . '"');
32
            }
33
        }
34 9
    }
35
36 9
    public function parse(?string $value): ?array
37
    {
38 9
        if ($value === null) {
39
            return null;
40
        }
41
42 9
        if (!preg_match('/^(?P<open>\[|\()(?P<lower>[^,]*),(?P<upper>[^\)\]]*)(?P<close>\)|\])$/', $value, $matches)) {
43
            throw new InvalidArgumentException();
44
        }
45
46 9
        $lower = $matches['lower'] ? trim($matches['lower'], '"') : null;
47 9
        $upper = $matches['upper'] ? trim($matches['upper'], '"') : null;
48 9
        $includeLower = $matches['open'] === '[';
49 9
        $includeUpper = $matches['close'] === ']';
50
51 9
        if ($lower === null && $upper === null) {
52
            return [null, null];
53
        }
54
55 9
        $type = $this->type ?? self::parseType($lower, $upper);
56
57 9
        switch ($type) {
58
            case Schema::TYPE_INT_4_RANGE:
59 3
                return self::parseIntRange($lower, $upper, $includeLower, $includeUpper);
60
            case Schema::TYPE_INT_8_RANGE:
61
                return self::parseBigIntRange($lower, $upper, $includeLower, $includeUpper);
62
            case Schema::TYPE_NUM_RANGE:
63 1
                return self::parseNumRange($lower, $upper, $includeLower, $includeUpper);
64
            case Schema::TYPE_DATE_RANGE:
65 3
                return self::parseDateRange($lower, $upper, $includeLower, $includeUpper);
66
            case Schema::TYPE_TS_RANGE:
67 1
                return self::parseTsRange($lower, $upper, $includeLower, $includeUpper);
68
            case Schema::TYPE_TS_TZ_RANGE:
69 1
                return self::parseTsTzRange($lower, $upper, $includeLower, $includeUpper);
70
            default:
71
                return null;
72
        }
73
    }
74
75 3
    private static function parseIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
76
    {
77 3
        $min = $lower === null ? null : (int) $lower;
78 3
        $max = $upper === null ? null : (int) $upper;
79
80 3
        if ($min !== null && $includeLower === false) {
81
            $min += 1;
82
        }
83
84 3
        if ($max !== null && $includeUpper === false) {
85 3
            $max -= 1;
86
        }
87
88 3
        return [$min, $max];
89
    }
90
91
    private static function parseBigIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
92
    {
93
        if (PHP_INT_SIZE === 8) {
94
            return self::parseIntRange($lower, $upper, $includeLower, $includeUpper);
95
        }
96
97
        [$min, $max] = self::parseNumRange($lower, $upper, $includeLower, $includeUpper);
98
99
        if ($min !== null && $includeLower === false) {
100
            $min += 1;
101
        }
102
103
        if ($max !== null && $includeUpper === false) {
104
            $max -= 1;
105
        }
106
107
        return [$min, $max];
108
    }
109
110 1
    private static function parseNumRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
0 ignored issues
show
Unused Code introduced by
The parameter $includeLower is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

110
    private static function parseNumRange(?string $lower, ?string $upper, /** @scrutinizer ignore-unused */ bool $includeLower, bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $includeUpper is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

110
    private static function parseNumRange(?string $lower, ?string $upper, bool $includeLower, /** @scrutinizer ignore-unused */ bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
111
    {
112 1
        $min = $lower === null ? null : (float) $lower;
113 1
        $max = $upper === null ? null : (float) $upper;
114
115 1
        return [$min, $max];
116
    }
117
118 3
    private static function parseDateRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
119
    {
120 3
        $interval = new DateInterval('P1D');
121 3
        $min = $lower ? DateTime::createFromFormat('Y-m-d', $lower) : null;
122 3
        $max = $upper ? DateTime::createFromFormat('Y-m-d', $upper) : null;
123
124 3
        if ($min && $includeLower === false) {
125
            $min->add($interval);
126
        }
127
128 3
        if ($max && $includeUpper === false) {
129 3
            $max->sub($interval);
130
        }
131
132 3
        return [$min, $max];
133
    }
134
135 1
    private static function parseTsRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
0 ignored issues
show
Unused Code introduced by
The parameter $includeLower is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

135
    private static function parseTsRange(?string $lower, ?string $upper, /** @scrutinizer ignore-unused */ bool $includeLower, bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $includeUpper is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

135
    private static function parseTsRange(?string $lower, ?string $upper, bool $includeLower, /** @scrutinizer ignore-unused */ bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
136
    {
137 1
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:s', $lower) : null;
138 1
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:s', $upper) : null;
139
140 1
        return [$min, $max];
141
    }
142
143 1
    private static function parseTsTzRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
0 ignored issues
show
Unused Code introduced by
The parameter $includeLower is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

143
    private static function parseTsTzRange(?string $lower, ?string $upper, /** @scrutinizer ignore-unused */ bool $includeLower, bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $includeUpper is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

143
    private static function parseTsTzRange(?string $lower, ?string $upper, bool $includeLower, /** @scrutinizer ignore-unused */ bool $includeUpper): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
144
    {
145 1
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:sP', $lower) : null;
146 1
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:sP', $upper) : null;
147
148 1
        return [$min, $max];
149
    }
150
151 41
    public static function isAllowedType(string $type): bool
152
    {
153 41
        return in_array($type, self::RANGES, true);
154
    }
155
156
    /**
157
     * Find range type from value format
158
     *
159
     * @param string $lower
160
     * @param string $upper
161
     *
162
     * @return string|null
163
     */
164
    private static function parseType(?string $lower, ?string $upper): ?string
165
    {
166
        if ($lower !== null && $upper !== null) {
167
            if (filter_var($lower, FILTER_VALIDATE_INT) && filter_var($upper, FILTER_VALIDATE_INT)) {
168
                return Schema::TYPE_INT_4_RANGE;
169
            }
170
171
            if (filter_var($lower, FILTER_VALIDATE_FLOAT) && filter_var($upper, FILTER_VALIDATE_FLOAT)) {
172
                return Schema::TYPE_NUM_RANGE;
173
            }
174
        }
175
176
        $value = $lower ?? $upper;
177
178
        if (filter_var($value, FILTER_VALIDATE_INT)) {
179
            return Schema::TYPE_INT_4_RANGE;
180
        }
181
182
183
        if (filter_var($value, FILTER_VALIDATE_FLOAT)) {
184
            return Schema::TYPE_NUM_RANGE;
185
        }
186
187
        if (DateTime::createFromFormat('Y-m-d', $value)) {
188
            return Schema::TYPE_DATE_RANGE;
189
        }
190
191
        if (DateTime::createFromFormat('Y-m-d H:i:s', $value)) {
192
            return Schema::TYPE_TS_RANGE;
193
        }
194
195
        if (DateTime::createFromFormat('Y-m-d H:i:sP', $value)) {
196
            return Schema::TYPE_TS_TZ_RANGE;
197
        }
198
199
        return null;
200
    }
201
}
202