Passed
Pull Request — master (#307)
by
unknown
03:26
created

RangeParser::parseIntRange()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 7
c 1
b 0
f 0
nc 16
nop 4
dl 0
loc 14
ccs 8
cts 8
cp 1
crap 7
rs 8.8333
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 38
    public function __construct(?string $type = null)
26
    {
27 38
        $this->type = $type;
28
    }
29
30 21
    public function withType(?string $type): self
31
    {
32 21
        $new = clone $this;
33 21
        $new->type = $type;
34
35 21
        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 2
    public function asTimestamp(): self
59
    {
60 2
        return $this->withType(Schema::TYPE_TS_RANGE);
61
    }
62
63 2
    public function asTimestampTz(): self
64
    {
65 2
        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 38
    public function parse(?string $value): ?array
74
    {
75 38
        if ($value === null || $value === 'empty') {
76 2
            return null;
77
        }
78
79 36
        if (!preg_match('/^(?P<open>\[|\()(?P<lower>[^,]*),(?P<upper>[^\)\]]*)(?P<close>\)|\])$/', $value, $matches)) {
80 7
            throw new InvalidArgumentException('Unsupported range format');
81
        }
82
83 29
        $lower = $matches['lower'] ? trim($matches['lower'], '"') : null;
84 29
        $upper = $matches['upper'] ? trim($matches['upper'], '"') : null;
85 29
        $includeLower = $matches['open'] === '[';
86 29
        $includeUpper = $matches['close'] === ']';
87
88 29
        if ($lower === null && $upper === null) {
89 3
            return [null, null];
90
        }
91
92 27
        return match($this->type) {
93 27
            Schema::TYPE_INT_RANGE => self::parseIntRange($lower, $upper, $includeLower, $includeUpper),
94 27
            Schema::TYPE_BIGINT_RANGE => self::parseBigIntRange($lower, $upper, $includeLower, $includeUpper),
95 27
            Schema::TYPE_NUM_RANGE => self::parseNumRange($lower, $upper),
96 27
            Schema::TYPE_DATE_RANGE => self::parseDateRange($lower, $upper, $includeLower, $includeUpper),
97 27
            Schema::TYPE_TS_RANGE => self::parseTsRange($lower, $upper),
98 27
            Schema::TYPE_TS_TZ_RANGE => self::parseTsTzRange($lower, $upper),
99 27
            default => [$lower, $upper]
100 27
        };
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
    private static function parseTsRange(?string $lower, ?string $upper): array
166
    {
167
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:s', $lower) : null;
168
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:s', $upper) : null;
169
170
        return [$min, $max];
171
    }
172
173
    private static function parseTsTzRange(?string $lower, ?string $upper): array
174
    {
175
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:sP', $lower) : null;
176
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:sP', $upper) : null;
177
178
        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