ConverterDefault::rangeToPattern()   C
last analyzed

Complexity

Conditions 7
Paths 16

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 32
c 0
b 0
f 0
ccs 22
cts 22
cp 1
rs 6.7272
cc 7
eloc 19
nc 16
nop 2
crap 7
1
<?php
2
3
namespace HansOtt\RangeRegex;
4
5
final class ConverterDefault implements Converter
6
{
7 6
    public function toRegex(Range $range)
8
    {
9 6
        $min = $range->getMin();
10 6
        $max = $range->getMax();
11
12 6
        if ($min === $max) {
13 2
            return $min;
14
        }
15
16 6
        if ($min > $max) {
17 2
            return sprintf('%d|%d', $min, $max);
18
        }
19
20 6
        $positives = [];
21 6
        $negatives = [];
22
23 6
        if ($min < 0) {
24 4
            $newMin = 1;
25 4
            if ($max < 0) {
26 2
                $newMin = abs($max);
27 2
            }
28
29 4
            $newMax = abs($min);
30 4
            $negatives = $this->splitToPatterns($newMin, $newMax);
31 4
            $min = 0;
32 4
        }
33
34 6
        if ($max >= 0) {
35 6
            $positives = $this->splitToPatterns($min, $max);
36 6
        }
37
38 6
        return $this->siftPatterns($negatives, $positives);
39
    }
40
41 6
    private function splitToRanges($min, $max)
42
    {
43 6
        $nines = 1;
44 6
        $stops = [$max];
45 6
        $stop = $this->countNines($min, $nines);
46
47 6
        while ($min <= $stop && $stop <= $max) {
48 6
            if (in_array($stop, $stops, true) === false) {
49 6
                $stops[] = $stop;
50 6
            }
51
52 6
            $nines++;
53 6
            $stop = $this->countNines($min, $nines);
54 6
        }
55
56 6
        $zeros = 1;
57 6
        $stop = $this->countZeros($max + 1, $zeros) - 1;
58
59 6
        while ($min < $stop && $stop <= $max) {
60 6
            if (in_array($stop, $stops, true) === false) {
61 4
                $stops[] = $stop;
62 4
            }
63
64 6
            $zeros += 1;
65 6
            $stop = $this->countZeros($max + 1, $zeros) - 1;
66 6
        }
67
68 6
        sort($stops);
69
70 6
        return $stops;
71
    }
72
73 6
    private function splitToPatterns($min, $max)
74
    {
75 6
        $start = $min;
76 6
        $subPatterns = [];
77 6
        $ranges = $this->splitToRanges($min, $max);
78 6
        foreach ($ranges as $index => $range) {
79 6
            $subPatterns[$index] = $this->rangeToPattern($start, $range);
80 6
            $start = $range + 1;
81 6
        }
82
83 6
        return $subPatterns;
84
    }
85
86 6
    private function siftPatterns(array $negatives, array $positives)
87
    {
88 6
        $onlyNegative = $this->filterPatterns($negatives, $positives, '-');
89 6
        $onlyPositives = $this->filterPatterns($positives, $negatives, '');
90 6
        $intersected = $this->filterPatterns($negatives, $positives, '-?', true);
91 6
        $subPatterns = array_merge($onlyNegative, $intersected, $onlyPositives);
92
93 6
        return implode('|', $subPatterns);
94
    }
95
96 6
    private function filterPatterns(array $arr, array $comparison, $prefix, $intersection = false)
97
    {
98 6
        $intersected = [];
99 6
        $result = [];
100 6
        foreach ($arr as $element) {
101 6
            if ($intersection === false && in_array($element, $comparison, true) === false) {
102 6
                $result[] = $prefix . $element;
103 6
            }
104
105 6
            if ($intersection && in_array($element, $comparison, true)) {
106 2
                $intersected[] = $prefix . $element;
107 2
            }
108 6
        }
109
110 6
        return $intersection ? $intersected : $result;
111
    }
112
113 6
    private function rangeToPattern($start, $stop)
114
    {
115 6
        $pattern = '';
116 6
        $digits = 0;
117 6
        $pairs = $this->zip($start, $stop);
118 6
        foreach ($pairs as $pair) {
119 6
            $startDigit = $pair[0];
120 6
            $stopDigit = $pair[1];
121
122 6
            if ($startDigit === $stopDigit) {
123 6
                $pattern .= $startDigit;
124 6
                continue;
125
            }
126
127 6
            if ($startDigit !== '0' || $stopDigit !== '9') {
128 6
                $pattern .= sprintf('[%d-%d]', $startDigit, $stopDigit);
129 6
                continue;
130
            }
131
132 6
            $digits++;
133 6
        }
134
135 6
        if ($digits > 0) {
136 6
            $pattern .= '[0-9]';
137 6
        }
138
139 6
        if ($digits > 1) {
140 6
            $pattern .= sprintf('{%d}', $digits);
141 6
        }
142
143 6
        return $pattern;
144
    }
145
146 6
    private function countNines($num, $nines)
147
    {
148 6
        $num = (string) $num;
149 6
        $offset = (-1) * $nines;
150
151 6
        return (int) (mb_substr($num, 0, $offset) . str_repeat('9', $nines));
152
    }
153
154 6
    private function countZeros($integer, $zeros)
155
    {
156 6
        return $integer - ($integer % pow(10, $zeros));
157
    }
158
159 6
    private function zip($start, $stop)
160
    {
161 6
        $start = (string) $start;
162 6
        $stop = (string) $stop;
163
164 6
        $start = str_split($start);
165 6
        $stop = str_split($stop);
166
167 6
        $zipped = [];
168 6
        foreach ($start as $index => $char) {
169 6
            $zipped[] = [$char, $stop[$index]];
170 6
        }
171
172 6
        return $zipped;
173
    }
174
}
175