Steps   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 82
dl 0
loc 273
ccs 79
cts 79
cp 1
rs 9.6
c 1
b 0
f 0
wmc 35

16 Methods

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