IntervalType::fromString()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.0218

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 8
cts 9
cp 0.8889
rs 9.6666
c 0
b 0
f 0
cc 4
nc 5
nop 1
crap 4.0218
1
<?php
2
3
/**
4
 * GpsLab component.
5
 *
6
 * @author    Peter Gribanov <[email protected]>
7
 * @copyright Copyright (c) 2016, Peter Gribanov
8
 * @license   http://opensource.org/licenses/MIT
9
 */
10
11
namespace GpsLab\Component\Interval;
12
13
use GpsLab\Component\Interval\Exception\IncorrectIntervalTypeException;
14
use GpsLab\Component\Interval\Exception\InvalidIntervalFormatException;
15
16
/**
17
 * @see https://en.wikipedia.org/wiki/Interval_(mathematics)
18
 */
19
final class IntervalType
20
{
21
    /**
22
     * @var int
23
     */
24
    const TYPE_START_EXCLUDED = 1;
25
26
    /**
27
     * @var int
28
     */
29
    const TYPE_END_EXCLUDED = 2;
30
31
    /**
32
     * [a, b] = [a, b] = {x ∈ ℝ | a ≤ x ≤ b}.
33
     *
34
     * @var int
35
     */
36
    const TYPE_CLOSED = 0;
37
38
    /**
39
     * [a, b) = [a, b[ = {x ∈ ℝ | a ≤ x < b}.
40
     *
41
     * @var int
42
     */
43
    const TYPE_HALF_CLOSED = self::TYPE_CLOSED | self::TYPE_END_EXCLUDED;
44
45
    /**
46
     * (a, b] = ]a, b] = {x ∈ ℝ | a < x ≤ b}.
47
     *
48
     * @var int
49
     */
50
    const TYPE_HALF_OPEN = self::TYPE_CLOSED | self::TYPE_START_EXCLUDED;
51
52
    /**
53
     * (a, b) = ]a, b[ = {x ∈ ℝ | a < x < b}.
54
     *
55
     * @var int
56
     */
57
    const TYPE_OPEN = self::TYPE_CLOSED | self::TYPE_START_EXCLUDED | self::TYPE_END_EXCLUDED;
58
59
    /**
60
     * @var string
61
     */
62
    const REGEXP = '/^
63
        (?<start>\(|\[) # start type char
64
        \s*
65
        .+              # start point
66
        \s*,\s*         # separator
67
        .+              # end point
68
        \s*
69
        (?<end>\)|\])   # end type char
70
    $/x';
71
72
    /**
73
     * @var IntervalType[]
74
     */
75
    private static $instances = [];
76
77
    /**
78
     * @var string
79
     */
80
    private $type = '';
81
82
    /**
83
     * @var array
84
     */
85
    private $formats = [
86
        self::TYPE_CLOSED => '[%s, %s]',
87
        self::TYPE_OPEN => '(%s, %s)',
88
        self::TYPE_HALF_CLOSED => '[%s, %s)',
89
        self::TYPE_HALF_OPEN => '(%s, %s]',
90
    ];
91
92
    /**
93
     * @param string $type
94
     */
95 4
    private function __construct($type)
96
    {
97 4
        $this->type = $type;
98 4
    }
99
100
    /**
101
     * @return int[]
102
     */
103 47
    public static function getAvailableValues()
104
    {
105
        return [
106 47
            self::TYPE_CLOSED,
107 47
            self::TYPE_OPEN,
108 47
            self::TYPE_HALF_CLOSED,
109 47
            self::TYPE_HALF_OPEN,
110
        ];
111
    }
112
113
    /**
114
     * @param int $value
115
     *
116
     * @return bool
117
     */
118 47
    public static function isValueSupported($value)
119
    {
120 47
        return in_array($value, self::getAvailableValues());
121
    }
122
123
    /**
124
     * @return int
125
     */
126 46
    public function type()
127
    {
128 46
        return $this->type;
129
    }
130
131
    /**
132
     * @param string $value
133
     *
134
     * @return self
135
     */
136 63
    private static function safe($value)
137
    {
138
        // limitation of count object instances
139 63
        if (!isset(self::$instances[$value])) {
140 4
            self::$instances[$value] = new self($value);
141
        }
142
143 63
        return self::$instances[$value];
144
    }
145
146
    /**
147
     * @param int $value
148
     *
149
     * @return self
150
     */
151 47
    public static function create($value)
152
    {
153 47
        if (!self::isValueSupported($value)) {
154
            throw IncorrectIntervalTypeException::create($value);
155
        }
156
157 47
        return self::safe($value);
158
    }
159
160
    /**
161
     * @return self
162
     */
163 9
    public static function closed()
164
    {
165 9
        return self::safe(self::TYPE_CLOSED);
166
    }
167
168
    /**
169
     * @return self
170
     */
171 1
    public static function halfClosed()
172
    {
173 1
        return self::safe(self::TYPE_HALF_CLOSED);
174
    }
175
176
    /**
177
     * @return self
178
     */
179 1
    public static function halfOpen()
180
    {
181 1
        return self::safe(self::TYPE_HALF_OPEN);
182
    }
183
184
    /**
185
     * @return self
186
     */
187 9
    public static function open()
188
    {
189 9
        return self::safe(self::TYPE_OPEN);
190
    }
191
192
    /**
193
     * Create interval type from string.
194
     *
195
     * All types format:
196
     *   [a, b]
197
     *   (a, b]
198
     *   [a, b)
199
     *   (a, b)
200
     *
201
     * Spaces are ignored in format.
202
     *
203
     * @param string $string
204
     *
205
     * @return self
206
     */
207 47
    public static function fromString($string)
208
    {
209 47
        if (!preg_match(self::REGEXP, $string, $match)) {
210
            throw InvalidIntervalFormatException::create('[a, b]', $string);
211
        }
212
213 47
        $type = self::TYPE_CLOSED;
214
215 47
        if ($match['start'] == '(') {
216 9
            $type |= self::TYPE_START_EXCLUDED;
217
        }
218
219 47
        if ($match['end'] == ')') {
220 9
            $type |= self::TYPE_END_EXCLUDED;
221
        }
222
223 47
        return self::create($type);
224
    }
225
226
    /**
227
     * @param IntervalType $type
228
     *
229
     * @return bool
230
     */
231
    public function equal(self $type)
232
    {
233
        return $this->type() == $type->type();
234
    }
235
236
    /**
237
     * @return bool
238
     */
239 31
    public function startExcluded()
240
    {
241 31
        return ($this->type() & self::TYPE_START_EXCLUDED) == self::TYPE_START_EXCLUDED;
242
    }
243
244
    /**
245
     * @return bool
246
     */
247 31
    public function endExcluded()
248
    {
249 31
        return ($this->type() & self::TYPE_END_EXCLUDED) == self::TYPE_END_EXCLUDED;
250
    }
251
252
    /**
253
     * @param string $start
254
     * @param string $end
255
     *
256
     * @return string
257
     */
258 12
    public function format($start, $end)
259
    {
260 12
        return sprintf($this->formats[$this->type()], $start, $end);
261
    }
262
263
    /**
264
     * @param IntervalInterface $interval
265
     *
266
     * @return string
267
     */
268 12
    public function formatInterval(IntervalInterface $interval)
269
    {
270 12
        return $this->format((string) $interval->startPoint(), (string) $interval->endPoint());
271
    }
272
273
    /**
274
     * @deprecated It's will be removed in later. You must use formatInterval()
275
     *
276
     * @param IntervalInterface $interval
277
     *
278
     * @return string
279
     */
280
    public function getReadable(IntervalInterface $interval)
281
    {
282
        trigger_error(
283
            'Method IntervalType::getReadable() is will be removed in later. '.
284
            'You must use IntervalType::formatInterval()',
285
            E_USER_DEPRECATED
286
        );
287
288
        return $this->formatInterval($interval);
289
    }
290
291
    /**
292
     * @return string
293
     */
294
    public function __toString()
295
    {
296
        return $this->format('a', 'b');
297
    }
298
}
299