Passed
Pull Request — master (#88)
by
unknown
03:21
created

RangeParser::parseDateRange()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7.0671

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 8
c 1
b 0
f 0
nc 16
nop 4
dl 0
loc 15
ccs 8
cts 9
cp 0.8889
crap 7.0671
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 RuntimeException;
11
use function preg_match;
12
13
final class RangeParser
14
{
15
    private const RANGES = [
16
        Schema::TYPE_INT_4_RANGE,
17
        Schema::TYPE_INT_8_RANGE,
18
        Schema::TYPE_NUM_RANGE,
19
        Schema::TYPE_TS_RANGE,
20
        Schema::TYPE_TS_TZ_RANGE,
21
        Schema::TYPE_DATE_RANGE,
22
    ];
23
24
    private ?string $type = null;
25
26 3
    public function __construct(?string $type = null)
27
    {
28 3
        if ($type !== null) {
29 3
            if (self::isAllowedType($type)) {
30 3
                $this->type = $type;
31
            } else {
32
                throw new InvalidArgumentException('Unsupported range type "' . $type . '"');
33
            }
34
        }
35 3
    }
36
37 3
    public function parse(?string $value): ?array
38
    {
39 3
        if ($value === null) {
40
            return null;
41
        }
42
43 3
        if (!preg_match('/^(\[|\()([^,]*),([^\)\]]*)(\)|\])$/', $value, $matches)) {
44
            throw new InvalidArgumentException();
45
        }
46
47 3
        $lower = $matches[2] ? trim($matches[2], '"') : null;
48 3
        $upper = $matches[3] ? trim($matches[3], '"') : null;
49 3
        $includeLower = $matches[1] === '[';
50 3
        $includeUpper = $matches[4] === ']';
51
52 3
        if ($lower === null && $upper === null) {
53
            return [null, null];
54
        }
55
56 3
        $type = $this->type ?? self::parseType($lower, $upper);
57
58 3
        switch ($type) {
59
            case Schema::TYPE_INT_4_RANGE:
60 3
                return self::parseIntRange($lower, $upper, $includeLower, $includeUpper);
61
            case Schema::TYPE_INT_8_RANGE:
62 3
                return self::parseBigIntRange($lower, $upper, $includeLower, $includeUpper);
63
            case Schema::TYPE_NUM_RANGE:
64 3
                return self::parseNumRange($lower, $upper, $includeLower, $includeUpper);
65
            case Schema::TYPE_DATE_RANGE:
66 3
                return self::parseDateRange($lower, $upper, $includeLower, $includeUpper);
67
            case Schema::TYPE_TS_RANGE:
68 3
                return self::parseTsRange($lower, $upper, $includeLower, $includeUpper);
69
            case Schema::TYPE_TS_TZ_RANGE:
70 3
                return self::parseTsTzRange($lower, $upper, $includeLower, $includeUpper);
71
            default:
72
                return null;
73
        }
74
    }
75
76 3
    private static function parseIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
77
    {
78 3
        $min = $lower === null ? null : (int) $lower;
79 3
        $max = $upper === null ? null : (int) $upper;
80
81 3
        if ($min !== null && $includeLower === false) {
82
            $min += 1;
83
        }
84
85 3
        if ($max !== null && $includeUpper === false) {
86 3
            $max -= 1;
87
        }
88
89 3
        return [$min, $max];
90
    }
91
92 3
    private static function parseBigIntRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
93
    {
94 3
        if (PHP_INT_SIZE === 4) {
95
            $min = $lower === null ? null : (float) $lower;
96
            $max = $upper === null ? null : (float) $upper;
97
        } else {
98 3
            $min = $lower === null ? null : (int) $lower;
99 3
            $max = $upper === null ? null : (int) $upper;
100
        }
101
102 3
        if ($min !== null && $includeLower === false) {
103
            $min += 1;
104
        }
105
106 3
        if ($max !== null && $includeUpper === false) {
107 3
            $max -= 1;
108
        }
109
110 3
        return [$min, $max];
111
    }
112
113 3
    private static function parseNumRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
0 ignored issues
show
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

113
    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...
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

113
    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...
114
    {
115 3
        $min = $lower === null ? null : (float) $lower;
116 3
        $max = $upper === null ? null : (float) $upper;
117
118 3
        return [$min, $max];
119
    }
120
121 3
    private static function parseDateRange(?string $lower, ?string $upper, bool $includeLower, bool $includeUpper): array
122
    {
123 3
        $interval = new DateInterval('P1D');
124 3
        $min = $lower ? DateTime::createFromFormat('Y-m-d', $lower) : null;
125 3
        $max = $upper ? DateTime::createFromFormat('Y-m-d', $upper) : null;
126
127 3
        if ($min && $includeLower === false) {
128
            $min->add($interval);
129
        }
130
131 3
        if ($max && $includeUpper === false) {
132 3
            $max->sub($interval);
133
        }
134
135 3
        return [$min, $max];
136
    }
137
138 3
    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

138
    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

138
    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...
139
    {
140 3
        $min = $lower ? DateTime::createFromFormat('Y-m-d H:i:s', $lower) : null;
141 3
        $max = $upper ? DateTime::createFromFormat('Y-m-d H:i:s', $upper) : null;
142
143 3
        return [$min, $max];
144
    }
145
146 3
    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

146
    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

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