StepExpression   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 57
c 1
b 0
f 1
dl 0
loc 212
ccs 62
cts 62
cp 1
rs 10
wmc 28

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A getSegments() 0 3 1
A resolveCountable() 0 3 1
A resolveSegmentCount() 0 18 4
A createFromString() 0 3 1
A validate() 0 9 2
A resolveCount() 0 9 3
A resolveSteps() 0 9 2
A resolveSegmentFunctor() 0 4 1
A parseSegment() 0 23 6
A parseSegments() 0 23 4
A verifyValueCount() 0 7 2
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 string $segment
54
     * @param int $count
55
     *
56
     * @return array|int[]
57
     *
58
     * @see resolveSegmentFunctor
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((int)$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((int)$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
    }
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
     *
127
     * @return array|Steps[]
128
     */
129 1
    public function resolveSteps(Steps $steps)
130
    {
131 1
        $stepNumbers = $this->resolveCountable($steps);
132 1
        $array = array();
133 1
        foreach ($stepNumbers as $number) {
134 1
            $array[] = $steps[$number - 1];
135
        }
136
137 1
        return $array;
138
    }
139
140
    /**
141
     * @param string $buffer
142
     *
143
     * @return array|string[]
144
     */
145 46
    private static function parseSegments($buffer)
146
    {
147 46
        $normalized = preg_replace(
148 46
            array('~\s+~', '~(\d+)\s+(-)~', '~(-)\s+(\d+)+~'),
149 46
            array(' ', '\\1\\2', '\\1\\2'),
150
            $buffer
151
        );
152
153 46
        if (empty($normalized)) {
154 3
            throw new \InvalidArgumentException(sprintf('No steps: "%s"', $buffer));
155
        }
156
157 43
        $result = preg_split('~\s*(?:,|\s)\s*~', $normalized, 64, PREG_SPLIT_NO_EMPTY);
158 43
        if (empty($result)) {
159 2
            throw new \InvalidArgumentException(sprintf('No steps: "%s"', $buffer));
160
        }
161
162 41
        $array = array();
163 41
        foreach ($result as $segmentBuffer) {
164 41
            $array[] = self::parseSegment($segmentBuffer);
165
        }
166
167 34
        return $array;
168
    }
169
170
    /**
171
     * Parse (better: normalize) segment
172
     *
173
     * @param string $buffer
174
     *
175
     * @return string
176
     */
177 41
    private static function parseSegment($buffer)
178
    {
179 41
        $segment = preg_replace('~\s+~', '', $buffer);
180 41
        if (null === $segment) {
181
            // @codeCoverageIgnoreStart
182
            throw new \InvalidArgumentException(sprintf('Can not parse step segment for "%s"', $buffer));
183
            // @codeCoverageIgnoreEnd
184
        }
185
186 41
        $result = preg_match('~^(?:(?:[1-9]\d*)?-(?:[1-9]\d*)?|(?:[1-9]\d*))$~', $segment);
187 41
        if (false === $result || 0 === $result || '-' === $segment) {
188 7
            if ($buffer !== $segment) {
189
                // @codeCoverageIgnoreStart
190
                throw new \InvalidArgumentException(
191
                    sprintf('Can not parse step segment for "%s" (from "%s")', $segment, $buffer)
192
                );
193
                // @codeCoverageIgnoreEnd
194
            }
195
196 7
            throw new \InvalidArgumentException(sprintf('Can not parse step segment for "%s"', $segment));
197
        }
198
199 34
        return $segment;
200
    }
201
202
    /**
203
     * @param int $count
204
     *
205
     * @return \Closure
206
     */
207 13
    private static function resolveSegmentFunctor($count)
208
    {
209 13
        return function ($segment) use ($count) {
210 13
            return StepExpression::resolveSegmentCount($segment, $count);
211
        };
212
    }
213
214
    /**
215
     * @param int $value
216
     * @param int $count
217
     *
218
     * @return int
219
     */
220 13
    private static function verifyValueCount($value, $count)
221
    {
222 13
        if ($value > $count) {
223 2
            throw new \InvalidArgumentException(sprintf('step %d is out of range, highest is %d', $value, $count));
224
        }
225
226 12
        return $value;
227
    }
228
}
229