Range::getRelativeDate()   B
last analyzed

Complexity

Conditions 10
Paths 6

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 15
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 19
ccs 0
cts 0
cp 0
crap 110
rs 7.6666

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 declare(strict_types = 1);
2
3
namespace Apicart\FQL\Token\Token;
4
5
use DateTimeZone;
6
use DateTimeImmutable;
7
use Apicart\FQL\Value\Token;
8
use InvalidArgumentException;
9
use Apicart\FQL\Tokenizer\Tokenizer;
10
11
final class Range extends Token
12
{
13
14
    public const TYPE_INCLUSIVE = 'inclusive';
15
16
    public const TYPE_EXCLUSIVE = 'exclusive';
17
18
    public const DATE_FORMAT = 'Y-m-d';
19
    public const DATE_REGEX = '/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/';
20
21
    public const DATETIME_FORMAT = 'Y-m-d\TH:i:s\Z';
22
    public const DATETIME_REGEX = '/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d{1,9})?Z$/';
23
24
    public const RELATIVE_DATE_SEPARATOR = '|';
25
    public const RELATIVE_DATE_REGEX = '/^(today|week|month|year)(\|(\+|-)?\d+)?$/';
26
    public const RELATIVE_DATE_TODAY = 'today';
27
    public const RELATIVE_DATE_WEEK = 'week';
28
    public const RELATIVE_DATE_MONTH = 'month';
29
    public const RELATIVE_DATE_YEAR = 'year';
30
    public const RELATIVE_DATE_VALUES = [
31
        self::RELATIVE_DATE_TODAY,
32
        self::RELATIVE_DATE_WEEK,
33
        self::RELATIVE_DATE_MONTH,
34
        self::RELATIVE_DATE_YEAR,
35
    ];
36
37
    /**
38
     * @var string
39
     */
40
    private $domain;
41
42
    /**
43
     * @var int|float|string
44
     */
45
    private $startValue;
46 23
47
    /**
48
     * @var int|float|string
49
     */
50
    private $endValue;
51
52
    /**
53
     * @var string|null
54
     */
55 23
    private $startType;
56 16
57 10
    /**
58
     * @var string|null
59 10
     */
60 10
    private $endType;
61 10
62 10
    /**
63 10
     * @var Flags|null
64 10
     */
65
    private $flags;
66
67 7
68
    /**
69 7
     * @param int|float|string $startValue
70
     * @param int|float|string $endValue
71
     */
72
    public function __construct(
73
        string $lexeme,
74
        int $position,
75
        string $domain,
76 6
        $startValue,
77
        $endValue,
78 6
        ?string $startType,
79
        ?string $endType,
80
        ?Flags $flags = null
81
    ) {
82
        $this->ensureValidType($startType);
83
        $this->ensureValidType($endType);
84
        parent::__construct(Tokenizer::TOKEN_TERM, $lexeme, $position);
85 5
86
        $this->domain = $domain;
87 5
        $this->startValue = $startValue;
88
        $this->endValue = $endValue;
89
        $this->startType = $startType;
90
        $this->endType = $endType;
91 7
        $this->flags = $flags;
92
    }
93 7
94
95
    public function getDomain(): string
96
    {
97 1
        return $this->domain;
98
    }
99 1
100 1
101
    /**
102
     * @return int|float|string
103 6
     */
104
    public function getStartValue()
105 6
    {
106
        return $this->startValue;
107
    }
108
109 1
110
    public function getStartDateValue(): ?DateTimeImmutable
111 1
    {
112 1
        if ($this->isStartInDateFormat()) {
113
            return DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $this->getStartValue());
114
        }
115 1
116
        return null;
117 1
    }
118
119
    public function getStartDateTimeValue(): ?DateTimeImmutable
120
    {
121 1
        if ($this->isStartInDateTimeFormat()) {
122
            return DateTimeImmutable::createFromFormat(self::DATETIME_FORMAT, $this->getStartValue());
123 1
        }
124
125
        return null;
126
    }
127 1
128
    public function getStartRelativeDateValue(): ?DateTimeImmutable
129 1
    {
130
        if ($this->isStartInRelativeDateFormat()) {
131
            return self::getRelativeDate($this->getStartValue());
132
        }
133 1
134
        return null;
135 1
    }
136
137
138
    /**
139 23
     * @return int|float|string
140
     */
141 23
    public function getEndValue()
142 13
    {
143
        return $this->endValue;
144 16
    }
145
146
    public function getEndDateValue(): ?DateTimeImmutable
147
    {
148
        if ($this->isEndInDateFormat()) {
149
            return DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $this->getEndValue());
150
        }
151
152
        return null;
153
    }
154
155
    public function getEndDateTimeValue(): ?DateTimeImmutable
156
    {
157
        if ($this->isEndInDateTimeFormat()) {
158
            return DateTimeImmutable::createFromFormat(self::DATETIME_FORMAT, $this->getEndValue());
159
        }
160
161
        return null;
162
    }
163
164
    
165
    public function getEndRelativeDateValue(): ?DateTimeImmutable
166
    {
167
        if ($this->isEndInRelativeDateFormat()) {
168
            return self::getRelativeDate($this->getEndValue());
169
        }
170
171
        return null;
172
    }
173
174
175
    public function getStartType(): ?string
176
    {
177
        return $this->startType;
178
    }
179
180
181
    public function setStartType(?string $startType): void
182
    {
183
        $this->startType = $startType;
184
    }
185
186
187
    public function getEndType(): ?string
188
    {
189
        return $this->endType;
190
    }
191
192
193
    public function setEndType(?string $endType): void
194
    {
195
        $this->endType = $endType;
196
    }
197
198
199
    public function getStartSign(): string
200
    {
201
        return $this->getStartType() === Range::TYPE_INCLUSIVE ? '>=' : '>';
202
    }
203
204
205
    public function getEndSign(): string
206
    {
207
        return $this->getEndType() === Range::TYPE_INCLUSIVE ? '<=' : '<';
208
    }
209
210
211
    public function isStartDefined(): bool
212
    {
213
        return $this->getStartValue() !== '*';
214
    }
215
216
217
    public function isEndDefined(): bool
218
    {
219
        return $this->getEndValue() !== '*';
220
    }
221
222
223
    public function getFlags(): ?Flags
224
    {
225
        return $this->flags;
226
    }
227
228
    public function isStartInDateFormat(): bool
229
    {
230
        return preg_match(self::DATE_REGEX, $this->getStartValue()) === 1;
231
    }
232
233
    public function isStartInDateTimeFormat(): bool
234
    {
235
        return preg_match(self::DATETIME_REGEX, $this->getStartValue()) === 1;
236
    }
237
238
    public function isStartInRelativeDateFormat(): bool
239
    {
240
        return preg_match(self::RELATIVE_DATE_REGEX, $this->getStartValue()) === 1;
241
    }
242
243
    public function isEndInDateFormat(): bool
244
    {
245
        return preg_match(self::DATE_REGEX, $this->getEndValue()) === 1;
246
    }
247
248
    public function isEndInDateTimeFormat(): bool
249
    {
250
        return preg_match(self::DATETIME_REGEX, $this->getEndValue()) === 1;
251
    }
252
253
    public function isEndInRelativeDateFormat(): bool
254
    {
255
        return preg_match(self::RELATIVE_DATE_REGEX, $this->getEndValue()) === 1;
256
    }
257
258
    /**
259
     * @return array{base: string, offset: int}|null
260
     */
261
    public static function parseRelativeDateValue(string $value): ?array
262
    {
263
        if (preg_match(self::RELATIVE_DATE_REGEX, $value) === 1) {
264
            $parts = explode(self::RELATIVE_DATE_SEPARATOR, $value);
265
            $base = $parts[0];
266
            $offset = isset($parts[1]) ? (int) $parts[1] : 0;
267
268
            return ['base' => $base, 'offset' => $offset];
269
        }
270
271
        return null;
272
    }
273
274
275
    public static function getRelativeDate(string $value): ?DateTimeImmutable
276
    {
277
        $parsed = self::parseRelativeDateValue($value);
278
        if ($parsed !== null) {
279
            $base = $parsed['base'];
280
            $offset = $parsed['offset'];
281
            $date = new DateTimeImmutable('now', new DateTimeZone('UTC'));
282
            switch ($base) {
283
                case self::RELATIVE_DATE_TODAY:
284
                    return $date->modify(($offset >= 0 ? '+' : '') . $offset . ' days');
285
                case self::RELATIVE_DATE_WEEK:
286
                    return $date->modify('this week')->modify(($offset >= 0 ? '+' : '') . $offset . ' weeks');
287
                case self::RELATIVE_DATE_MONTH:
288
                    return $date->modify('first day of this month')->modify(($offset >= 0 ? '+' : '') . $offset . ' months');
289
                case self::RELATIVE_DATE_YEAR:
290
                    return $date->modify('first day of january this year')->modify(($offset >= 0 ? '+' : '') . $offset . ' years');
291
            }
292
        }
293
        return null;
294
    }
295
296
    private function ensureValidType(?string $type): void
297
    {
298
        if (! in_array($type, [self::TYPE_EXCLUSIVE, self::TYPE_INCLUSIVE], true)) {
299
            throw new InvalidArgumentException(sprintf('Invalid range type: %s', $type));
300
        }
301
    }
302
303
}
304