Test Failed
Pull Request — master (#307)
by
unknown
04:01
created

RangeParser::parseBigIntRange()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 8
c 1
b 0
f 0
nc 5
nop 4
dl 0
loc 19
rs 9.2222
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
    public function __construct(?string $type = null)
26
    {
27
        $this->type = $type;
28
    }
29
30
    public function withType(?string $type): self
31
    {
32
        $new = clone $this;
33
        $new->type = $type;
34
35
        return $new;
36
    }
37
38
    public function asInt(): self
39
    {
40
        return $this->withType(Schema::TYPE_INT_RANGE);
41
    }
42
43
    public function asBigInt(): self
44
    {
45
        return $this->withType(Schema::TYPE_BIGINT_RANGE);
46
    }
47
48
    public function asNumeric(): self
49
    {
50
        return $this->withType(Schema::TYPE_NUM_RANGE);
51
    }
52
53
    public function asDate(): self
54
    {
55
        return $this->withType(Schema::TYPE_DATE_RANGE);
56
    }
57
58
    public function asTimestamp(): self
59
    {
60
        return $this->withType(Schema::TYPE_TS_RANGE);
61
    }
62
63
    public function asTimestampTz(): self
64
    {
65
        return $this->withType(Schema::TYPE_TS_TZ_RANGE);
66
    }
67
68
    public function asCustom(): self
69
    {
70
        return $this->withType(null);
71
    }
72
73
    public function parse(?string $value): ?array
74
    {
75
        if ($value === null || $value === 'empty') {
76
            return null;
77
        }
78
79
        if (!preg_match('/^(?P<open>\[|\()(?P<lower>[^,]*),(?P<upper>[^\)\]]*)(?P<close>\)|\])$/', $value, $matches)) {
80
            throw new InvalidArgumentException('Unsupported range format');
81
        }
82
83
        $lower = $matches['lower'] ? trim($matches['lower'], '"') : null;
84
        $upper = $matches['upper'] ? trim($matches['upper'], '"') : null;
85
        $includeLower = $matches['open'] === '[';
86
        $includeUpper = $matches['close'] === ']';
87
88
        if ($lower === null && $upper === null) {
89
            return [null, null];
90
        }
91
92
        return match($this->type) {
93
            Schema::TYPE_INT_RANGE => self::parseIntRange($lower, $upper, $includeLower, $includeUpper),
94
            Schema::TYPE_BIGINT_RANGE => self::parseBigIntRange($lower, $upper, $includeLower, $includeUpper),
95
            Schema::TYPE_NUM_RANGE => self::parseNumRange($lower, $upper),
96
            Schema::TYPE_DATE_RANGE => self::parseDateRange($lower, $upper, $includeLower, $includeUpper),
97
            Schema::TYPE_TS_RANGE => self::parseTsRange($lower, $upper),
98
            Schema::TYPE_TS_TZ_RANGE => self::parseTsTzRange($lower, $upper),
99
            default => [$lower, $upper]
100
        };
101
    }
102
103
    private static function parseIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
104
    {
105
        $min = $lower === null ? null : (int) $lower;
106
        $max = $upper === null ? null : (int) $upper;
107
108
        if ($min !== null && $includeLower === false) {
109
            $min += 1;
110
        }
111
112
        if ($max !== null && $includeUpper === false) {
113
            $max -= 1;
114
        }
115
116
        return [$min, $max];
117
    }
118
119
    private static function parseBigIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
120
    {
121
        if (PHP_INT_SIZE === 8) {
122
            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
    private static function parseNumRange(?string $lower, ?string $upper): array
141
    {
142
        $min = $lower === null ? null : (float) $lower;
143
        $max = $upper === null ? null : (float) $upper;
144
145
        return [$min, $max];
146
    }
147
148
    private static function parseDateRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
149
    {
150
        $interval = new DateInterval('P1D');
151
        $min = $lower ? DateTime::createFromFormat('Y-m-d', $lower) : null;
152
        $max = $upper ? DateTime::createFromFormat('Y-m-d', $upper) : null;
153
154
        if ($min && $includeLower === false) {
155
            $min->add($interval);
156
        }
157
158
        if ($max && $includeUpper === false) {
159
            $max->sub($interval);
160
        }
161
162
        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
    public static function isAllowedType(string $type): bool
182
    {
183
        return in_array($type, self::RANGES, true);
184
    }
185
}
186