Passed
Push — steps ( c7b5f4...d19fba )
by Tom
02:52
created

StepExpression::resolveSteps()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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