Passed
Push — master ( 1d257e...9a2f5c )
by Chema
01:12 queued 13s
created

SegmentFactory::classImplements()   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
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 5
rs 10
ccs 3
cts 3
cp 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EdifactParser\Segments;
6
7
use function class_implements;
8
use function in_array;
9
use InvalidArgumentException;
10
use Webmozart\Assert\Assert;
11
12
/** @psalm-immutable */
13
final class SegmentFactory implements SegmentFactoryInterface
14
{
15
    public const DEFAULT_SEGMENTS = [
16
        'UNH' => UNHMessageHeader::class,
17
        'DTM' => DTMDateTimePeriod::class,
18
        'NAD' => NADNameAddress::class,
19
        'MEA' => MEADimensions::class,
20
        'CNT' => CNTControl::class,
21
        'PCI' => PCIPackageId::class,
22
        'BGM' => BGMBeginningOfMessage::class,
23
        'UNT' => UNTMessageFooter::class,
24
    ];
25
26
    private const TAG_LENGTH = 3;
27
28
    /**
29
     * The list of "segment class names" for every segment that might be created.
30
     *
31
     * @var array<string,string>
32
     */
33
    private array $segments;
34
35
    /**
36
     * @psalm-pure
37
     *
38
     * @param array<string,string> $segments
39
     * The key: The 'Segment Tag' -> A three-character (alphanumeric) that identifies the segment.
40
     * The value: The class that will be created once that 'Segment Tag' is found. It must implement
41
     * the `SegmentInterface` in order to be able to work with the factory, otherwise it will be ignored.
42
     */
43 2
    public static function withSegments(array $segments): self
44
    {
45 2
        return new self($segments);
46
    }
47
48
    /** @psalm-pure */
49 10
    public static function withDefaultSegments(): self
50
    {
51 10
        return new self(self::DEFAULT_SEGMENTS);
52
    }
53
54
    /** @param array<string,string> $segments */
55 12
    private function __construct(array $segments)
56
    {
57 12
        Assert::allLength(array_keys($segments), self::TAG_LENGTH);
58
59
        array_map(function (string $class): void {
60 12
            if (!$this->classImplements($class, SegmentInterface::class)) {
61 1
                throw new InvalidArgumentException("'{$class}' must implements 'SegmentInterface'");
62
            }
63 12
        }, $segments);
64
65 11
        $this->segments = $segments;
66 11
    }
67
68 10
    public function segmentFromArray(array $rawArray): SegmentInterface
69
    {
70 10
        $tag = (string) $rawArray[0];
71 10
        Assert::length($tag, self::TAG_LENGTH);
72 10
        $className = $this->segments[$tag] ?? '';
73
74 10
        if (!empty($className)) {
75 10
            $segment = new $className($rawArray);
76 10
            Assert::isInstanceOf($segment, SegmentInterface::class);
77
78 10
            return $segment;
79
        }
80
81 5
        return new UnknownSegment($rawArray);
82
    }
83
84 12
    private function classImplements(string $className, string $interface): bool
85
    {
86 12
        $interfaces = class_implements($className);
87
88 12
        return $interfaces && in_array($interface, class_implements($className));
0 ignored issues
show
Bug Best Practice introduced by
The expression $interfaces of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
89
    }
90
}
91