Passed
Pull Request — master (#307)
by
unknown
04:21
created

RangeParser   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Test Coverage

Coverage 92.5%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
eloc 70
c 1
b 0
f 0
dl 0
loc 172
ccs 74
cts 80
cp 0.925
rs 8.64

17 Methods

Rating   Name   Duplication   Size   Complexity  
A parseBigIntRange() 0 19 6
A withType() 0 6 1
A asDate() 0 3 1
B parseDateRange() 0 15 7
B parse() 0 27 8
A asBigInt() 0 3 1
A __construct() 0 3 1
A asTimestampTz() 0 3 1
A isAllowedType() 0 3 1
A parseNumRange() 0 6 3
B parseIntRange() 0 14 7
A asTimestamp() 0 3 1
A asInt() 0 3 1
A asNumeric() 0 3 1
A asCustom() 0 3 1
A parseTsRange() 0 6 3
A parseTsTzRange() 0 6 3

How to fix   Complexity   

Complex Class

Complex classes like RangeParser 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 RangeParser, and based on these observations, apply Extract Interface, too.

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_RANGE,
16
        Schema::TYPE_BIGINT_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;
24
25 46
    public function __construct(?string $type = null)
26
    {
27 46
        $this->type = $type;
28
    }
29
30 27
    public function withType(?string $type): self
31
    {
32 27
        $new = clone $this;
33 27
        $new->type = $type;
34
35 27
        return $new;
36
    }
37
38 8
    public function asInt(): self
39
    {
40 8
        return $this->withType(Schema::TYPE_INT_RANGE);
41
    }
42
43 4
    public function asBigInt(): self
44
    {
45 4
        return $this->withType(Schema::TYPE_BIGINT_RANGE);
46
    }
47
48 6
    public function asNumeric(): self
49
    {
50 6
        return $this->withType(Schema::TYPE_NUM_RANGE);
51
    }
52
53 2
    public function asDate(): self
54
    {
55 2
        return $this->withType(Schema::TYPE_DATE_RANGE);
56
    }
57
58 5
    public function asTimestamp(): self
59
    {
60 5
        return $this->withType(Schema::TYPE_TS_RANGE);
61
    }
62
63 5
    public function asTimestampTz(): self
64
    {
65 5
        return $this->withType(Schema::TYPE_TS_TZ_RANGE);
66
    }
67
68 9
    public function asCustom(): self
69
    {
70 9
        return $this->withType(null);
71
    }
72
73 46
    public function parse(?string $value): ?array
74
    {
75 46
        if ($value === null || $value === 'empty') {
76 2
            return null;
77
        }
78
79 44
        if (!preg_match('/^(?P<open>\[|\()(?P<lower>[^,]*),(?P<upper>[^\)\]]*)(?P<close>\)|\])$/', $value, $matches)) {
80 7
            throw new InvalidArgumentException('Unsupported range format');
81
        }
82
83 37
        $lower = $matches['lower'] ? trim($matches['lower'], '"') : null;
84 37
        $upper = $matches['upper'] ? trim($matches['upper'], '"') : null;
85 37
        $includeLower = $matches['open'] === '[';
86 37
        $includeUpper = $matches['close'] === ']';
87
88 37
        if ($lower === null && $upper === null) {
89 3
            return [null, null];
90
        }
91
92 35
        return match($this->type) {
93 35
            Schema::TYPE_INT_RANGE => self::parseIntRange($lower, $upper, $includeLower, $includeUpper),
94 35
            Schema::TYPE_BIGINT_RANGE => self::parseBigIntRange($lower, $upper, $includeLower, $includeUpper),
95 35
            Schema::TYPE_NUM_RANGE => self::parseNumRange($lower, $upper),
96 35
            Schema::TYPE_DATE_RANGE => self::parseDateRange($lower, $upper, $includeLower, $includeUpper),
97 35
            Schema::TYPE_TS_RANGE => self::parseTsRange($lower, $upper),
98 35
            Schema::TYPE_TS_TZ_RANGE => self::parseTsTzRange($lower, $upper),
99 35
            default => [$lower, $upper]
100 35
        };
101
    }
102
103 12
    private static function parseIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
104
    {
105 12
        $min = $lower === null ? null : (int) $lower;
106 12
        $max = $upper === null ? null : (int) $upper;
107
108 12
        if ($min !== null && $includeLower === false) {
109 1
            $min += 1;
110
        }
111
112 12
        if ($max !== null && $includeUpper === false) {
113 9
            $max -= 1;
114
        }
115
116 12
        return [$min, $max];
117
    }
118
119 4
    private static function parseBigIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
120
    {
121 4
        if (PHP_INT_SIZE === 8) {
122 4
            return self::parseIntRange($lower, $upper, $includeLower, $includeUpper);
123
        }
124
125
        [$min, $max] = self::parseNumRange($lower, $upper);
126
127
        if ($min !== null && $includeLower === false) {
128
            /** @var float $min */
129
            $min += 1;
130
        }
131
132
        if ($max !== null && $includeUpper === false) {
133
            /** @var float $max */
134
            $max -= 1;
135
        }
136
137
        return [$min, $max];
138
    }
139
140 6
    private static function parseNumRange(?string $lower, ?string $upper): array
141
    {
142 6
        $min = $lower === null ? null : (float) $lower;
143 6
        $max = $upper === null ? null : (float) $upper;
144
145 6
        return [$min, $max];
146
    }
147
148 6
    private static function parseDateRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
149
    {
150 6
        $interval = new DateInterval('P1D');
151 6
        $min = $lower ? DateTime::createFromFormat('Y-m-d', $lower) : null;
152 6
        $max = $upper ? DateTime::createFromFormat('Y-m-d', $upper) : null;
153
154 6
        if ($min && $includeLower === false) {
155 2
            $min->add($interval);
156
        }
157
158 6
        if ($max && $includeUpper === false) {
159 4
            $max->sub($interval);
160
        }
161
162 6
        return [$min, $max];
163
    }
164
165 4
    private static function parseTsRange(?string $lower, ?string $upper): array
166
    {
167 4
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:s', $lower) : null;
168 4
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:s', $upper) : null;
169
170 4
        return [$min, $max];
171
    }
172
173 4
    private static function parseTsTzRange(?string $lower, ?string $upper): array
174
    {
175 4
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:sP', $lower) : null;
176 4
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:sP', $upper) : null;
177
178 4
        return [$min, $max];
179
    }
180
181 96
    public static function isAllowedType(string $type): bool
182
    {
183 96
        return in_array($type, self::RANGES, true);
184
    }
185
}
186