Passed
Push — test ( 4577ec...4fb8ad )
by Tom
02:38
created

Pipelines::searchTypeReference()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 2
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\File;
6
7
use InvalidArgumentException;
8
use Ktomk\Pipelines\Glob;
9
use Ktomk\Pipelines\Lib;
10
use Ktomk\Pipelines\Runner\Reference;
11
12
/**
13
 * Class Pipelines
14
 *
15
 * @package Ktomk\Pipelines\File
16
 */
17
class Pipelines
18
{
19
    /**
20
     * @var array
21
     */
22
    private $array;
23
    /**
24
     * @var null|File
25
     */
26
    private $file;
27
28
    /**
29
     * @var array
30
     */
31
    private $pipelines;
32
33 18
    public function __construct(array $array, File $file = null)
34
    {
35 18
        $this->file = $file;
36
37 18
        $this->pipelines = $this->parsePipelineReferences($array);
38
39 14
        $this->array = $array;
40 14
    }
41
42
    /**
43
     * @return null|Pipeline
44
     */
45 2
    public function getDefault()
46
    {
47 2
        return $this->getById('default');
48
    }
49
50
    /**
51
     * returns the id of the default pipeline in file or null if there is none
52
     *
53
     * @return null|string
54
     */
55 2
    public function getIdDefault()
56
    {
57 2
        $id = 'default';
58
59 2
        if (!isset($this->pipelines[$id])) {
60 1
            return null;
61
        }
62
63 1
        return $id;
64
    }
65
66
    /**
67
     * @return array
68
     */
69 3
    public function getPipelineIds()
70
    {
71 3
        return array_keys($this->pipelines);
72
    }
73
74
    /**
75
     * @param string $id
76
     *
77
     * @throws InvalidArgumentException
78
     * @throws ParseException
79
     *
80
     * @return null|Pipeline
81
     */
82 9
    public function getById($id)
83
    {
84 9
        if (!ReferenceTypes::isValidId($id)) {
85 1
            throw new InvalidArgumentException(sprintf("Invalid id '%s'", $id));
86
        }
87
88 8
        if (!isset($this->pipelines[$id])) {
89 1
            return null;
90
        }
91
92 7
        $ref = $this->pipelines[$id];
93 7
        if ($ref[2] instanceof Pipeline) {
94 2
            return $ref[2];
95
        }
96
97
        // bind to instance if yet an array
98 7
        if (!is_array($ref[2])) {
99 1
            throw new ParseException(sprintf('%s: named pipeline required', $id));
100
        }
101 6
        $pipeline = new Pipeline($this->file, $ref[2]);
0 ignored issues
show
Bug introduced by
It seems like $this->file can also be of type null; however, parameter $file of Ktomk\Pipelines\File\Pipeline::__construct() does only seem to accept Ktomk\Pipelines\File\File, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $pipeline = new Pipeline(/** @scrutinizer ignore-type */ $this->file, $ref[2]);
Loading history...
102 6
        $ref[2] = $pipeline;
103
104 6
        return $pipeline;
105
    }
106
107
    /**
108
     * get id of a pipeline
109
     *
110
     * @param Pipeline $pipeline
111
     *
112
     * @return null|string
113
     */
114 2
    public function getId(Pipeline $pipeline)
115
    {
116 2
        foreach ($this->pipelines as $id => $reference) {
117 2
            if ($pipeline === $reference[2]) {
118 1
                return $id;
119
            }
120
        }
121
122 1
        return null;
123
    }
124
125
    /**
126
     * Searches a reference
127
     *
128
     * @param Reference $reference
129
     *
130
     * @throws ParseException
131
     * @throws \UnexpectedValueException
132
     * @throws InvalidArgumentException
133
     *
134
     * @return null|Pipeline
135
     */
136 7
    public function searchReference(Reference $reference)
137
    {
138 7
        if (null === $type = $reference->getPipelinesType()) {
139 2
            return $this->getDefault();
140
        }
141
142 5
        return $this->searchTypeReference($type, $reference->getName());
143
    }
144
145
    /**
146
     * Searches a reference within type, returns found one, if
147
     * none is found, the default pipeline or null if there is
148
     * no default pipeline.
149
     *
150
     * @param string $type of pipeline, can be branches, tags or bookmarks
151
     * @param null|string $reference
152
     *
153
     * @throws ParseException
154
     * @throws \UnexpectedValueException
155
     * @throws InvalidArgumentException
156
     *
157
     * @return null|Pipeline
158
     */
159 8
    public function searchTypeReference($type, $reference)
160
    {
161 8
        $id = $this->searchIdByTypeReference($type, $reference);
162
163 7
        return null !== $id ? $this->getById($id) : null;
164
    }
165
166
    /**
167
     * Searches the pipeline that matches the reference
168
     *
169
     * @param Reference $reference
170
     *
171
     * @throws \UnexpectedValueException
172
     * @throws InvalidArgumentException
173
     *
174
     * @return null|string id if found, null otherwise
175
     */
176 1
    public function searchIdByReference(Reference $reference)
177
    {
178 1
        if (null === $reference->getType()) {
179 1
            return $this->getIdDefault();
180
        }
181
182 1
        return $this->searchIdByTypeReference(
183 1
            $reference->getPipelinesType(),
184 1
            $reference->getName()
185
        );
186
    }
187
188
    /**
189
     * @return File
190
     */
191 1
    public function getFile()
192
    {
193 1
        if ($this->file instanceof File) {
194 1
            return $this->file;
195
        }
196
197 1
        throw new \BadMethodCallException('Unassociated node');
198
    }
199
200
    /**
201
     * @throws InvalidArgumentException
202
     * @throws ParseException
203
     *
204
     * @return array|Pipeline[]
205
     */
206 2
    public function getPipelines()
207
    {
208
        // FIXME(tk): IteratorAggregate + PipelinesIterator
209 2
        $pipelines = array();
210
211 2
        foreach ($this->getPipelineIds() as $id) {
212 2
            if (!ReferenceTypes::isValidId($id)) {
213 1
                throw new ParseException(sprintf("invalid pipeline id '%s'", $id));
214
            }
215 1
            $pipelines[$id] = $this->getById($id);
216
        }
217
218 1
        return $pipelines;
219
    }
220
221
    /**
222
     * Index all pipelines as references array map
223
     *
224
     * @param array $array
225
     *
226
     * @throws ParseException
227
     *
228
     * @return array
229
     */
230 18
    private function parsePipelineReferences(array &$array)
231
    {
232
        // quick validation: pipeline sections
233 18
        $sections = ReferenceTypes::getSections();
234 18
        $count = 0;
235 18
        foreach ($sections as $section) {
236 18
            if (isset($array[$section])) {
237 12
                $count++;
238
            }
239
        }
240 18
        if (!$count && !isset($array['default'])) {
241 2
            $middle = implode(', ', array_slice($sections, 0, -1));
242
243 2
            throw new ParseException("'pipelines' requires at least a default, ${middle} or custom section");
244
        }
245
246
        $references = array();
247
248
        // default pipeline
249
        $section = 'default';
250
        if (isset($array[$section])) {
251
            if (!is_array($array[$section])) {
252
                throw new ParseException("'${section}' requires a list of steps");
253
            }
254
            $references[$section] = array(
255
                $section,
256
                null,
257
                &$array[$section],
258
            );
259
        }
260
261
        // section pipelines
262 15
        foreach ($array as $section => $refs) {
263 15
            if (!in_array($section, $sections, true)) {
264 10
                continue; // not a section for references
265
            }
266 12
            if (!is_array($refs)) {
267 1
                throw new ParseException("'${section}' requires a list");
268
            }
269 11
            foreach ($refs as $pattern => $pipeline) {
270 11
                $references["${section}/${pattern}"] = array(
271 11
                    (string)$section,
272 11
                    (string)$pattern,
273 11
                    &$array[$section][$pattern],
274
                );
275
            }
276
        }
277
278 14
        return $references;
279
    }
280
281
    /**
282
     * @param null|string $type
283
     * @param null|string $reference
284
     *
285
     * @throws \UnexpectedValueException
286
     * @throws InvalidArgumentException
287
     *
288
     * @return null|string
289
     */
290
    private function searchIdByTypeReference($type, $reference)
291
    {
292 5
        if (!ReferenceTypes::isPatternSection($type)) {
293 1
            throw new InvalidArgumentException(sprintf('Invalid type %s', var_export($type, true)));
294
        }
295
296 4
        if (!isset($this->array[$type])) {
297 2
            return $this->getIdDefault();
298
        }
299 3
        $array = &$this->array[$type];
300
301
        # check for direct (non-pattern) match
302 3
        if (isset($array[$reference])) {
303 2
            return "${type}/${reference}";
304
        }
305
306
        # get entry with largest pattern to match
307 2
        $patterns = array_keys($array);
308 2
        unset($array);
309
310 2
        $match = '';
311 2
        foreach ($patterns as $pattern) {
312 2
            $pattern = (string)$pattern;
313 2
            $result = Glob::match($pattern, $reference);
314 2
            if ($result && (strlen($pattern) > strlen($match))) {
315 1
                $match = $pattern;
316
            }
317
        }
318 2
        if ('' !== $match) {
319 1
            return "${type}/${match}";
320
        }
321
322 1
        return $this->getIdDefault();
323
    }
324
}
325