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
|
|||
90 | } |
||
91 | } |
||
92 |
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.