Issues (2)

src/Segments/SegmentFactory.php (1 issue)

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
    /** @var array<string,string> */
16
    public const DEFAULT_SEGMENTS = [
17
        'UNH' => UNHMessageHeader::class,
18
        'DTM' => DTMDateTimePeriod::class,
19
        'NAD' => NADNameAddress::class,
20
        'MEA' => MEADimensions::class,
21
        'CNT' => CNTControl::class,
22
        'PCI' => PCIPackageId::class,
23
        'BGM' => BGMBeginningOfMessage::class,
24
        'UNT' => UNTMessageFooter::class,
25
    ];
26
27
    private const TAG_LENGTH = 3;
28
29
    /**
30
     * The list of "segment class names" for every segment that might be created.
31
     *
32
     * @var array<string,string>
33
     */
34
    private array $segments;
35
36
    /**
37
     * @psalm-pure
38
     *
39
     * @param array<string,string> $segments
40
     * The key: The 'Segment Tag' -> A three-character (alphanumeric) that identifies the segment.
41
     * The value: The class that will be created once that 'Segment Tag' is found. It must implement
42
     * the `SegmentInterface` in order to be able to work with the factory, otherwise it will be ignored.
43
     */
44 3
    public static function withSegments(array $segments): self
45
    {
46 3
        return new self($segments);
47
    }
48
49
    /** @psalm-pure */
50 15
    public static function withDefaultSegments(): self
51
    {
52 15
        return new self(self::DEFAULT_SEGMENTS);
53
    }
54
55
    /** @param array<string,string> $segments */
56 18
    private function __construct(array $segments)
57
    {
58 18
        foreach ($segments as $tag => $class) {
59 18
            Assert::length($tag, self::TAG_LENGTH);
60
61 17
            if (!$this->classImplements($class, SegmentInterface::class)) {
62 1
                throw new InvalidArgumentException("'{$class}' must implements 'SegmentInterface'");
63
            }
64
        }
65
66 16
        $this->segments = $segments;
67 16
    }
68
69 15
    public function createSegmentFromArray(array $rawArray): SegmentInterface
70
    {
71 15
        $tag = (string) $rawArray[0];
72 15
        Assert::length($tag, self::TAG_LENGTH);
73 15
        $className = $this->segments[$tag] ?? '';
74
75 15
        if (empty($className)) {
76 10
            return new UnknownSegment($rawArray);
77
        }
78
79 15
        $segment = new $className($rawArray);
80 15
        Assert::isInstanceOf($segment, SegmentInterface::class);
81
82 15
        return $segment;
83
    }
84
85 17
    private function classImplements(string $className, string $interface): bool
86
    {
87 17
        $interfaces = class_implements($className);
88
89 17
        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...
90
    }
91
}
92