Passed
Push — test ( b084c8...32ef82 )
by Tom
02:29
created

StepExpression::verifyValueCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 1
b 0
f 1
nc 2
nop 2
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Value;
6
7
use Ktomk\Pipelines\Lib;
8
9
/**
10
 * Class StepExpression
11
 *
12
 * @package Ktomk\Pipelines\Value
13
 */
14
class StepExpression
15
{
16
    /**
17
     * @var array 1-3, -5, 1-
18
     */
19
    private $segments;
20
21
    /**
22
     * @param string $expression
23
     *
24
     * @return StepExpression
25
     */
26 31
    public static function createFromString($expression)
27
    {
28 31
        return new self(self::parseSegments($expression));
29
    }
30
31
    /**
32
     * Is a step expression syntactically valid?
33
     *
34
     * @param string $expression
35
     *
36
     * @return bool
37
     */
38 14
    public static function validate($expression)
39
    {
40
        try {
41 14
            self::parseSegments($expression);
42 4
        } catch (\InvalidArgumentException $ex) {
43 4
            return false;
44
        }
45
46 10
        return true;
47
    }
48
49
    /**
50
     * @param $buffer
51
     *
52
     * @return array|string[]
53
     */
54 45
    private static function parseSegments($buffer)
55
    {
56 45
        $normalized = preg_replace(array('~\s+~', '~(\d+)\s+(-)~', '~(-)\s+(\d+)+~'), array(' ', '\\1\\2', '\\1\\2'), $buffer);
57 45
        if (empty($normalized)) {
58 3
            throw new \InvalidArgumentException(sprintf('No steps: "%s"', $buffer));
59
        }
60
61 42
        $result = preg_split('~\s*(?:,|\s)\s*~', $normalized, 64, PREG_SPLIT_NO_EMPTY);
62 42
        if (empty($result)) {
63 2
            throw new \InvalidArgumentException(sprintf('No steps: "%s"', $buffer));
64
        }
65
66 40
        $array = array();
67 40
        foreach ($result as $segmentBuffer) {
68 40
            $array[] = self::parseSegment($segmentBuffer);
69
        }
70
71 33
        return $array;
72
    }
73
74
    /**
75
     * Parse (better: normalize) segment
76
     *
77
     * @param string $buffer
78
     *
79
     * @return string
80
     */
81 40
    private static function parseSegment($buffer)
82
    {
83 40
        $segment = preg_replace('~\s+~', '', $buffer);
84 40
        if (null === $segment) {
85
            // @codeCoverageIgnoreStart
86
            throw new \InvalidArgumentException(sprintf('Can not parse step segment for "%s"', $buffer));
87
            // @codeCoverageIgnoreEnd
88
        }
89
90 40
        $result = preg_match('~^(?:(?:[1-9]\d*)?-(?:[1-9]\d*)?|(?:[1-9]\d*))$~', $segment);
91 40
        if (false === $result || 0 === $result || '-' === $segment) {
92 7
            if ($buffer !== $segment) {
93
                // @codeCoverageIgnoreStart
94
                throw new \InvalidArgumentException(sprintf('Can not parse step segment for "%s" (from "%s")', $segment, $buffer));
95
                // @codeCoverageIgnoreEnd
96
            }
97 7
            throw new \InvalidArgumentException(sprintf('Can not parse step segment for "%s"', $segment));
98
        }
99
100 33
        return $segment;
101
    }
102
103
    /**
104
     * StepExpression constructor.
105
     *
106
     * @param array $segments
107
     */
108 24
    public function __construct(array $segments)
109
    {
110 24
        $this->segments = $segments;
111 24
    }
112
113
    /**
114
     * @return array
115
     */
116 10
    public function getSegments()
117
    {
118 10
        return $this->segments;
119
    }
120
121
    /**
122
     * @param mixed $countable
123
     *
124
     * @return mixed
125
     */
126 12
    public function resolveCountable($countable)
127
    {
128 12
        return $this->resolveCount(count($countable));
129
    }
130
131
    /**
132
     * @param int $count
133
     */
134 13
    public function resolveCount($count)
135
    {
136 13
        if (0 === $count || !is_int($count)) {
137 1
            throw new \InvalidArgumentException('Count is zero');
138
        }
139
140 12
        $range = Lib::merge(array_map(self::resolveSegmentFunctor($count), $this->segments));
141 10
        return call_user_func_array('array_merge', $range);
142
    }
143
144
    /**
145
     * @param int $count
146
     */
147
    private static function resolveSegmentFunctor($count)
148
    {
149 12
        return function ($segment) use ($count) {
150 12
            return StepExpression::resolveSegmentCount($segment, $count);
151 12
        };
152
    }
153
154 12
    public static function resolveSegmentCount($segment, $count)
155
    {
156 12
        $pos = strpos($segment, '-');
157 12
        list($from, $to) = explode('-', $segment, 2) + array('', '');
158
159 12
        if (false === $pos) {
160 4
            return array(self::verifyValueCount((int)Lib::emptyCoalesce($from, $to), $count));
161
        }
162
163 8
        if (empty($to)) {
164 3
            return range(self::verifyValueCount($from, $count), $count);
165
        }
166
167 7
        if (empty($from)) {
168 3
            return range(self::verifyValueCount(1, $count), self::verifyValueCount($to, $count));
169
        }
170
171 4
        return range(self::verifyValueCount($from, $count), self::verifyValueCount($to, $count));
172
    }
173
174
    /**
175
     * @param int $value
176
     * @param int $count
177
     *
178
     * @return int
179
     */
180 12
    private static function verifyValueCount($value, $count)
181
    {
182 12
        if ($value > $count) {
183 2
            throw new \InvalidArgumentException(sprintf('%d is out of range (%d)', $value, $count));
184
        }
185
186 11
        return $value;
187
    }
188
}
189