Passed
Push — steps ( 2bf0cd...c7b5f4 )
by Tom
04:13
created

StepExpression::createFromString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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