AnalysisResult::addArbitraryLimit()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 12
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 12
loc 12
ccs 1
cts 1
cp 1
rs 9.4285
cc 1
eloc 9
nc 1
nop 5
crap 1
1
<?php
2
/**
3
 * AnalysisResult.php
4
 *
5
 * MIT LICENSE
6
 *
7
 * LICENSE: This source file is subject to the MIT license.
8
 * A copy of the licenses text was distributed alongside this
9
 * file (usually the repository or package root). The text can also
10
 * be obtained through one of the following sources:
11
 * * http://opensource.org/licenses/MIT
12
 * * https://github.com/suralc/pvra/blob/master/LICENSE
13
 *
14
 * @author     suralc <[email protected]>
15
 * @license    http://opensource.org/licenses/MIT  MIT
16
 */
17
namespace Pvra;
18
19
20
use Pvra\Result\MessageFormatter;
21
use Pvra\Result\Reason;
22
use Pvra\Result\Reasoning;
23
24
/**
25
 * Class AnalysisResult
26
 *
27
 * @package Pvra
28
 */
29
class AnalysisResult implements \IteratorAggregate, \Countable
30
{
31
    const INITIAL_ANALYSIS_TARGET_ID = 'unknown';
32
    const VERSION_LIMIT_MIN = 0;
33
    const VERSION_LIMIT_MAX = 1;
34
35
    /**
36
     * The state of this instance
37
     *
38
     * @var bool
39
     */
40
    private $isSealed = false;
41
42
    /**
43
     * @var string Filename or hash of input string
44
     */
45
    private $analysisTargetId = self::INITIAL_ANALYSIS_TARGET_ID;
46
47
    /**
48
     * @var array|Reasoning[]
49
     */
50
    private $requirements = [];
51
52
    /**
53
     * @var array|Reasoning[]
54
     */
55
    private $limits = [];
56
57
    /**
58
     * @var string|null
59
     */
60
    private $cachedVersionRequirement;
61
62
    /**
63
     * @var string|null
64
     */
65
    private $cachedVersionLimit;
66
67
    /**
68
     * @var int
69
     */
70
    private $cachedRequiredVersionId;
71
72
    /**
73
     * @var int
74
     */
75
    private $cachedLimitedVersionId;
76
77
    /**
78
     * Number of attached `Reasonings` instances.
79
     *
80
     * Related to the `\Countable` interface.
81
     *
82
     * @var int
83
     * @see Pvra\AnalysisResult::count Accessable through count
84
     */
85
    private $count = 0;
86
87
    /**
88
     * @var MessageFormatter
89
     */
90
    private $msgFormatter;
91
92
    /**
93
     * Calculate the id of the required version
94
     *
95
     * Creates an integer representation of a version in the format "a.b[.c]".
96
     * The third version element is optional and can be omitted. A default value of "0" will be
97
     * assumed.
98
     *
99
     * @return int
100
     * @throws \Exception
101
     */
102 22
    public function getRequiredVersionId()
103
    {
104 22
        if ($this->cachedRequiredVersionId === null) {
105 22
            $this->cachedRequiredVersionId = $this->calculateVersionIdFromString($this->getRequiredVersion());
106 10
        }
107
108 20
        return $this->cachedRequiredVersionId;
109
    }
110
111 2
    public function getVersionLimitId()
112
    {
113 2
        if ($this->cachedLimitedVersionId === null) {
114 2
            $this->cachedLimitedVersionId = $this->calculateVersionIdFromString($this->getVersionLimit());
115 1
        }
116
117 2
        return $this->cachedLimitedVersionId;
118
    }
119
120
    /**
121
     * @param string $version
122
     * @return int
123
     * @throws \Exception
124
     */
125 24
    private function calculateVersionIdFromString($version)
126
    {
127 24
        $versionComponents = explode('.', $version);
128
129 24
        $elementCount = count($versionComponents);
130 24
        if ($elementCount > 3 || $elementCount < 2) {
131 2
            throw new \Exception(sprintf('A version id has to be built from two or three segments. "%s" is not valid.',
132 1
                $version));
133
        }
134
135 22
        $versionComponents += [2 => 0];
136
137 22
        return $versionComponents[0] * 10000 + $versionComponents[1] * 100 + $versionComponents[2];
138
    }
139
140
    /**
141
     * Get the attached message formatter
142
     *
143
     * If no message formatter has been set a default one will be created assuming default values.
144
     *
145
     * @return \Pvra\Result\MessageFormatter
146
     */
147 50
    public function getMsgFormatter()
148
    {
149 50
        if ($this->msgFormatter === null) {
150 6
            $this->msgFormatter = new MessageFormatter();
151 3
        }
152
153 50
        return $this->msgFormatter;
154
    }
155
156
    /**
157
     * @param \Pvra\Result\MessageFormatter $formatter
158
     * @return $this
159
     */
160 48
    public function setMsgFormatter(MessageFormatter $formatter)
161
    {
162 48
        $this->msgFormatter = $formatter;
163
164 48
        return $this;
165
    }
166
167
    /**
168
     * Retrieve the required version
169
     *
170
     * This method calculates the highest required version of all known requirements.
171
     * If no changes were made between the calls to this method the version requirement will
172
     * not be recalculated.
173
     *
174
     * @return string The required version in the format `Major.Minor[.Patch]`
175
     * @see http://php.net/manual/en/function.version-compare.php version_compare()
176
     */
177 110
    public function getRequiredVersion()
178
    {
179 110
        if ($this->cachedVersionRequirement !== null) {
180 10
            return $this->cachedVersionRequirement;
181
        }
182
183 110
        $keys = array_keys($this->requirements);
184
185 110
        if (!empty($keys)) {
186
            return $this->cachedVersionRequirement = \Pvra\Console\get_array_max_value($keys, 'version_compare');
187 30
        }
188 102
189
        return '5.2.0';
190 102
    }
191
192
    /**
193 12
     * Retrieve the upper version limit
194
     *
195
     * This method calculates the upper version limit of all known reasonings.
196
     * If no changes were made between the calls to this method the version limit will
197
     * not be recalculated.
198
     *
199
     * @return string The version limit in the format `Major.Minor[.Patch]`
200
     * @see http://php.net/manual/en/function.version-compare.php version_compare()
201
     */
202
    public function getVersionLimit()
203
    {
204
        if ($this->cachedVersionLimit !== null) {
205
            return $this->cachedVersionLimit;
206 14
        }
207
208 14
        $keys = array_keys($this->limits);
209 2
210
        if (!empty($keys)) {
211
            return $this->cachedVersionLimit = \Pvra\Console\get_array_min_value($keys, 'version_compare');
212 14
        }
213
214 14
        return '8.0.0';
215
    }
216 4
217 14
    /**
218
     * Add an arbitrary requirement identified by version
219 14
     *
220
     * This method can be used to add an arbitrary requirement. All parameters but the first are optional
221
     *
222 2
     * @param string $version The version in the format `Major.Minor[.Patch]`
223
     * @param int $line The line that caused the requirement.
224
     * @param string $msg The message template that should be used. If `null` is passed the attached `MessageLocator`
225
     *     will be called to retrieve a template based on the `$reason` parameter.
226
     * @param int $reason The reason for this requirement. Please be aware: Setting this parameter will **not**
227
     *     override the required version
228
     * @param array $data Additional data that should be passed to the message formatter.
229
     * @return $this
230
     */
231 View Code Duplication
    public function addArbitraryRequirement(
232
        $version,
233
        $line = -1,
234
        $msg = null,
235
        $reason = Reason::UNKNOWN,
236
        array $data = []
237
    ) {
238
        $this->addArbitraryVersionConstraint(self::VERSION_LIMIT_MAX, $version, $line, $msg, $reason,
239 62
            $data);
240
241
        return $this;
242
    }
243
244
    /**
245
     * Add a requirement identified by reason id
246 62
     *
247 31
     * This method can be used to add a requirement that is identified by its reason id.
248
     *
249 58
     * @param int $reason The reason for this requirement. The required version is determined from this id.
250
     * @param int $line The line that caused the requirement.
251
     * @param string $msg The message template that should be used. If `null` is passed the attached `MessageLocator`
252
     *     will be called to retrieve a template based on the `$reason` parameter.
253
     * @param array $data Additional data that should be passed to the message formatter.
254
     * @return $this
255
     * @throws \LogicException Thrown if the reason is unknown or does not have a version requirement associated.
256
     */
257 View Code Duplication
    public function addRequirement($reason, $line = -1, $msg = null, array $data = [])
258
    {
259
        $version = Reason::getVersionFromReason($reason);
260
261
        if ($version === false) {
262
            throw new \LogicException(sprintf('%s::%s requires a reason a version can be associated to. Use %s::addArbitraryRequirement() to add any version with any reasoning to the result.',
263
                __CLASS__, __METHOD__, __CLASS__));
264
        }
265 98
266
        $this->addArbitraryVersionConstraint(self::VERSION_LIMIT_MAX, $version, $line, $msg, $reason,
267 98
            $data);
268
269 98
        return $this;
270 2
    }
271 2
272
    /**
273
     * @param int $reason
274 96
     * @param int $line
275 48
     * @param null|string $msg
276
     * @param array $data
277 96
     * @return $this
278
     */
279 View Code Duplication
    public function addLimit($reason, $line = -1, $msg = null, array $data = [])
280
    {
281
        $version = Reason::getVersionFromReason($reason);
282
283
        if ($version === false) {
284
            throw new \LogicException(sprintf('%s::%s requires a reason a version can be associated to. Use %s::addArbitraryLimit() to add any version with any reasoning to the result.',
285
                __CLASS__, __METHOD__, __CLASS__));
286
        }
287 14
288
        $this->addArbitraryVersionConstraint(self::VERSION_LIMIT_MIN, $version, $line, $msg, $reason,
289 14
            $data);
290
291 14
        return $this;
292 2
    }
293 2
294
    /**
295
     * @param string $version
296 12
     * @param int $line
297 6
     * @param null|string $msg
298
     * @param int $reason
299 12
     * @param array $data
300
     * @return $this
301
     */
302 View Code Duplication
    public function addArbitraryLimit(
303
        $version,
304
        $line = -1,
305
        $msg = null,
306
        $reason = Reason::UNKNOWN,
307
        array $data = []
308
    ) {
309
        $this->addArbitraryVersionConstraint(self::VERSION_LIMIT_MIN, $version, $line, $msg, $reason,
310 28
            $data);
311
312
        return $this;
313
    }
314
315
    /**
316
     * @param int $type
317 28
     * @param string $version
318 14
     * @param int $line
319
     * @param null|string $msg
320 28
     * @param int $reason
321
     * @param array $data
322
     */
323
    protected function addArbitraryVersionConstraint(
324
        $type,
325
        $version,
326
        $line = -1,
327
        $msg = null,
328
        $reason = Reason::UNKNOWN,
329
        array $data = []
330
    ) {
331 158
        if ($this->isSealed()) {
332
            throw new \RuntimeException('Impossible to write to already sealed result');
333
        }
334
335
        $this->clearInstanceCaches();
336
        $this->count++;
337
338
        if ($type === self::VERSION_LIMIT_MAX) {
339 158
            $this->requirements[ $version ][] = new Reasoning($reason, $line, $this, $version, $msg, $data);
340 4
        } elseif ($type === self::VERSION_LIMIT_MIN) {
341
            $this->limits[ $version ][] = new Reasoning($reason, $line, $this, $version, $msg, $data);
342
        }
343 154
    }
344 154
345
    /**
346 154
     * @return bool
347 136
     */
348 97
    public function isSealed()
349 40
    {
350 20
        return $this->isSealed;
351 154
    }
352
353
    /**
354
     * @return array|\Pvra\Result\Reasoning[]
355
     */
356 184
    public function getRequirements()
357
    {
358 184
        return $this->requirements;
359
    }
360
361
    /**
362
     * @return array|\Pvra\Result\Reasoning[]
363
     */
364 148
    public function getLimits()
365
    {
366 148
        return $this->limits;
367
    }
368
369
    /**
370
     * Get all reasonings related to a version
371
     *
372 114
     * If no reasoning for a version is known an empty array will be returned.
373
     *
374 114
     * @param string $version Version in the format `Major.Minor.Patch`
375
     * @return array|Reasoning[] List of `Reasoning` or empty array
376
     */
377
    public function getRequirementInfo($version)
378
    {
379
        if (isset($this->requirements[ $version ])) {
380
            return $this->requirements[ $version ];
381
        }
382
383
        return [];
384
    }
385 10
386
    /**
387 10
     * @param string $version
388 10
     * @return array|\Pvra\Result\Reasoning[]
389
     */
390
    public function getLimitInfo($version)
391 4
    {
392
        if (isset($this->limits[ $version ])) {
393
            return $this->limits[ $version ];
394
        }
395
396
        return [];
397
    }
398 6
399
    /**
400 6
     * Get the current analysis target id
401 4
     *
402
     * @return string Analysis target id
403
     */
404 2
    public function getAnalysisTargetId()
405
    {
406
        return $this->analysisTargetId;
407
    }
408
409
    /**
410
     * @param string $analysisTargetId
411
     * @return $this
412 90
     */
413
    public function setAnalysisTargetId($analysisTargetId)
414 90
    {
415
        if ($this->isSealed() || $this->getAnalysisTargetId() !== self::INITIAL_ANALYSIS_TARGET_ID) {
416
            throw new \RuntimeException('You cannot modify an already set or sealed result.');
417
        }
418
419
        $this->analysisTargetId = $analysisTargetId;
420
421 82
        return $this;
422
    }
423 82
424 4
    /**
425
     *
426
     */
427 80
    public function seal()
428
    {
429 80
        $this->isSealed = true;
430
    }
431
432
    /**
433
     * @inheritdoc
434
     * @return \ArrayIterator|array|Reasoning[]
435 56
     */
436
    public function getIterator()
437 56
    {
438 56
        $iterator = new \ArrayIterator();
439
        $data = [$this->getRequirements(), $this->getLimits()];
440
        array_walk_recursive($data, function ($value) use ($iterator) {
441
            if ($value instanceof Reasoning) {
442
                $iterator->append($value);
443
            }
444 90
        });
445
446 90
        return $iterator;
447 90
    }
448 90
449 68
    /**
450 68
     * @return \ArrayIterator
451 34
     */
452 90 View Code Duplication
    public function getLimitIterator()
453
    {
454 90
        $iterator = new \ArrayIterator();
455
        foreach ($this->getLimits() as $version) {
456
            /** @var Reasoning $item */
457
            foreach ($version as $item) {
458
                if ($item instanceof Reasoning) {
459
                    $iterator->append($item);
460 2
                }
461
            }
462 2
        }
463 2
464
        return $iterator;
465 2
    }
466 2
467 2
    /**
468 1
     * @return \ArrayIterator
469 1
     */
470 1 View Code Duplication
    public function getRequirementIterator()
471
    {
472 2
        $iterator = new \ArrayIterator();
473
        foreach ($this->getRequirements() as $version) {
474
            /** @var Reasoning $item */
475
            foreach ($version as $item) {
476
                if ($item instanceof Reasoning) {
477
                    $iterator->append($item);
478 6
                }
479
            }
480 6
        }
481 6
482
        return $iterator;
483 6
    }
484 6
485 6
    /**
486 3
     * Number of registered reasonings
487 3
     *
488 3
     * @return int
489
     */
490 6
    public function count()
491
    {
492
        return $this->count;
493
    }
494
495
    /**
496
     * Clear the cached max version requirements
497
     */
498 104
    private function clearInstanceCaches()
499
    {
500 104
        $this->cachedVersionRequirement = null;
501
        $this->cachedRequiredVersionId = null;
502
        $this->cachedLimitedVersionId = null;
503
        $this->cachedVersionLimit = null;
504
    }
505
}
506