Passed
Push — steps ( cace08...74de34 )
by Tom
02:51
created

Steps::parseStep()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 3
nc 2
nop 3
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\File\Pipeline;
6
7
use Ktomk\Pipelines\File\ParseException;
8
use Ktomk\Pipelines\File\Pipeline;
9
10
/**
11
 * Class Steps
12
 *
13
 * A Pipeline consist of Steps. Some of them can be parallel.
14
 */
15
class Steps implements \ArrayAccess, \Countable, \IteratorAggregate
16
{
17
    /**
18
     * @var Pipeline
19
     */
20
    private $pipeline;
21
22
    /**
23
     * @var array pipeline definition
24
     */
25
    private $array;
26
27
    /**
28
     * @var array|Step[] steps of the pipeline
29
     * @see parseSteps
30
     */
31
    private $steps = array();
32
33
    /**
34
     * @var callable
35
     */
36
    private $getIteratorFunctor;
37
38
    /**
39
     * Get full iteration of steps' steps
40
     *
41
     * Gracefully handles null for steps.
42
     *
43
     * @param $steps
44
     *
45
     * @return StepsIterator
46
     */
47 1
    public static function fullIter($steps)
48
    {
49 1
        if (null === $steps) {
50 1
            return new StepsIterator(new \ArrayIterator(array()));
51
        }
52
53 1
        if (!$steps instanceof Steps) {
54 1
            throw new \InvalidArgumentException('Invalid steps argument');
55
        }
56
57 1
        $iter = $steps->getIterator();
58 1
        $iter->setNoManual(true);
59
60 1
        return $iter;
61
    }
62
63
    /**
64
     * Pipeline constructor.
65
     *
66
     * @param Pipeline $pipeline
67
     * @param array $definition
68
     *
69
     * @throws ParseException
70
     */
71 7
    public function __construct(Pipeline $pipeline, array $definition)
72
    {
73
        // quick validation
74 7
        if (!isset($definition[0])) {
75 1
            ParseException::__('Steps requires a tree of steps');
76
        }
77
78 7
        $this->pipeline = $pipeline;
79 7
        $this->parseSteps($definition);
80 7
    }
81
82
    /**
83
     * @return Pipeline
84
     */
85 1
    public function getPipeline()
86
    {
87 1
        return $this->pipeline;
88
    }
89
90
    /**
91
     * @return array|Step[]
92
     */
93 4
    public function getSteps()
94
    {
95 4
        return $this->steps;
96
    }
97
98
    /**
99
     * @param $functor
100
     *
101
     * @see getIterator
102
     */
103 1
    public function setGetIteratorFunctor($functor)
104
    {
105 1
        $this->getIteratorFunctor = $functor;
106 1
    }
107
108
    /**
109
     * Specify data which should be serialized to JSON
110
     *
111
     * @return array
112
     * @since 5.4.0
113
     */
114 1
    public function jsonSerialize()
115
    {
116 1
        $steps = array();
117 1
        foreach ($this->getSteps() as $step) {
118 1
            $steps[] = $step->jsonSerialize();
119
        }
120
121
        return array(
122 1
            'steps' => $steps,
123
        );
124
    }
125
126
    /* @see \ArrayAccess */
127
128 1
    public function offsetExists($offset)
129
    {
130 1
        return isset($this->steps[$offset]);
131
    }
132
133
    /**
134
     * @param mixed $offset
135
     *
136
     * @return Step
137
     */
138 1
    public function offsetGet($offset)
139
    {
140 1
        return $this->steps[$offset];
141
    }
142
143 1
    public function offsetSet($offset, $value)
144
    {
145 1
        throw new \BadMethodCallException('Steps offsets are read-only');
146
    }
147
148 1
    public function offsetUnset($offset)
149
    {
150 1
        throw new \BadMethodCallException('Steps offsets are read-only');
151
    }
152
153
    /* @see \Countable */
154
155 2
    public function count()
156
    {
157 2
        return count($this->steps);
158
    }
159
160
    /* @see \IteratorAggregate */
161
162
    /**
163
     * @return Step[]|StepsIterator
164
     */
165 2
    public function getIterator()
166
    {
167 2
        if (is_callable($this->getIteratorFunctor)) {
168 1
            return new StepsIterator(
169 1
                call_user_func($this->getIteratorFunctor, $this)
170
            );
171
        }
172
173 2
        return new StepsIterator(
174 2
            new \ArrayIterator($this->steps)
175
        );
176
    }
177
178 7
    private function parseSteps(array $definition)
179
    {
180 7
        $this->array = array();
181 7
        $this->steps = array();
182
183 7
        foreach ($definition as $node) {
184 7
            if (!is_array($node)) {
185 1
                ParseException::__(sprintf('Step expected, got %s', gettype($node)));
186
            }
187 7
            if (empty($node)) {
188 1
                ParseException::__('Step expected, got empty array');
189
            }
190
191 7
            $keys = array_keys($node);
192 7
            $name = $keys[0];
193 7
            if (!in_array($name, array('step', 'parallel'), true)) {
194 1
                ParseException::__(sprintf("Unexpected pipeline property '%s', expected 'step' or 'parallel'", $name));
195
            }
196
197 7
            $this->parseNode($node, $name);
198
        }
199 7
    }
200
201
    /**
202
     * @param int $index of step, from the zero based index in the list of steps
203
     * @param array $step
204
     * @param array $env [optional] environment variables in array notation for the new step
205
     *
206
     * @return Step
207
     */
208 7
    private function parseStep($index, array $step, array $env = array())
209
    {
210 7
        if (0 === $index && isset($step['trigger']) && 'manual' === $step['trigger']) {
211 1
            ParseException::__("The first step of a pipeline can't be manually triggered");
212
        }
213
214 7
        return new Step($this->pipeline, $index, $step, $env);
215
    }
216
217
    /**
218
     * @param array $node
219
     * @param $name
220
     */
221 7
    private function parseNode(array $node, $name)
222
    {
223 7
        $this->array[] = $node;
224
        switch ($name) {
225 7
            case 'step':
226 7
                $this->steps[] = $this->parseStep(count($this->steps), $node[$name]);
227
228 7
                break;
229 3
            case 'parallel':
230 3
                $this->parallel($node[$name]);
231
232 2
                break;
233
            default:
234
                // @codeCoverageIgnoreStart
235
                throw new \BadMethodCallException(
236
                    sprintf('Unchecked name condition: "%s"', $name)
237
                );
238
            // @codeCoverageIgnoreEnd
239
        }
240 7
    }
241
242
    /**
243
     * @param $node
244
     */
245 3
    private function parallel(array $node)
246
    {
247 3
        $group = array();
248 3
        foreach ($node as $step) {
249 3
            if (!(isset($step['step']) && is_array($step['step']))) {
250 1
                ParseException::__('Parallel step must consist of steps only');
251
            }
252 3
            $group[] = $step['step'];
253
        }
254
255 3
        $total = count($group);
256 3
        foreach ($group as $index => $step) {
257 3
            $this->steps[] = $this->parseStep(
258 3
                count($this->steps),
259 3
                $step,
260
                array(
261 3
                    'BITBUCKET_PARALLEL_STEP' => $index,
262 3
                    'BITBUCKET_PARALLEL_STEP_COUNT' => $total,
263
                )
264
            );
265
        }
266 2
    }
267
}
268