Skip to content

Commit

Permalink
Merge pull request #10 from Chemaclass/refactor-segment-factory
Browse files Browse the repository at this point in the history
Refactor SegmentFactory
  • Loading branch information
Chemaclass committed Jun 5, 2020
2 parents 1d257e2 + a955a8c commit 9a2f5cc
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .php_cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ return PhpCsFixer\Config::create()
'order' => 'alpha',
],
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_align' => true,
'phpdoc_align' => false,
'phpdoc_indent' => true,
'phpdoc_no_access' => true,
'phpdoc_no_empty_return' => true,
Expand Down
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
allowStringToStandInForClass="true"
>
<projectFiles>
<directory name="src"/>
Expand Down
2 changes: 1 addition & 1 deletion src/EdifactParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class EdifactParser

public static function create(?SegmentFactoryInterface $segmentFactory = null): self
{
return new self($segmentFactory ?? new SegmentFactory());
return new self($segmentFactory ?? SegmentFactory::withDefaultSegments());
}

private function __construct(SegmentFactoryInterface $segmentFactory)
Expand Down
2 changes: 1 addition & 1 deletion src/SegmentList.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class SegmentList
/** @psalm-pure */
public static function factory(?SegmentFactoryInterface $segmentFactory = null): self
{
return new self($segmentFactory ?? new SegmentFactory());
return new self($segmentFactory ?? SegmentFactory::withDefaultSegments());
}

private function __construct(SegmentFactoryInterface $segmentFactory)
Expand Down
91 changes: 74 additions & 17 deletions src/Segments/SegmentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,87 @@

namespace EdifactParser\Segments;

use function class_implements;
use function in_array;
use InvalidArgumentException;
use Webmozart\Assert\Assert;

/** @psalm-immutable */
final class SegmentFactory implements SegmentFactoryInterface
{
public const DEFAULT_SEGMENTS = [
'UNH' => UNHMessageHeader::class,
'DTM' => DTMDateTimePeriod::class,
'NAD' => NADNameAddress::class,
'MEA' => MEADimensions::class,
'CNT' => CNTControl::class,
'PCI' => PCIPackageId::class,
'BGM' => BGMBeginningOfMessage::class,
'UNT' => UNTMessageFooter::class,
];

private const TAG_LENGTH = 3;

/**
* The list of "segment class names" for every segment that might be created.
*
* @var array<string,string>
*/
private array $segments;

/**
* @psalm-pure
*
* @param array<string,string> $segments
* The key: The 'Segment Tag' -> A three-character (alphanumeric) that identifies the segment.
* The value: The class that will be created once that 'Segment Tag' is found. It must implement
* the `SegmentInterface` in order to be able to work with the factory, otherwise it will be ignored.
*/
public static function withSegments(array $segments): self
{
return new self($segments);
}

/** @psalm-pure */
public static function withDefaultSegments(): self
{
return new self(self::DEFAULT_SEGMENTS);
}

/** @param array<string,string> $segments */
private function __construct(array $segments)
{
Assert::allLength(array_keys($segments), self::TAG_LENGTH);

array_map(function (string $class): void {
if (!$this->classImplements($class, SegmentInterface::class)) {
throw new InvalidArgumentException("'{$class}' must implements 'SegmentInterface'");
}
}, $segments);

$this->segments = $segments;
}

public function segmentFromArray(array $rawArray): SegmentInterface
{
switch ($rawArray[0]) {
case 'UNH':
return new UNHMessageHeader($rawArray);
case 'DTM':
return new DTMDateTimePeriod($rawArray);
case 'NAD':
return new NADNameAddress($rawArray);
case 'MEA':
return new MEADimensions($rawArray);
case 'CNT':
return new CNTControl($rawArray);
case 'PCI':
return new PCIPackageId($rawArray);
case 'BGM':
return new BGMBeginningOfMessage($rawArray);
case 'UNT':
return new UNTMessageFooter($rawArray);
$tag = (string) $rawArray[0];
Assert::length($tag, self::TAG_LENGTH);
$className = $this->segments[$tag] ?? '';

if (!empty($className)) {
$segment = new $className($rawArray);
Assert::isInstanceOf($segment, SegmentInterface::class);

return $segment;
}

return new UnknownSegment($rawArray);
}

private function classImplements(string $className, string $interface): bool
{
$interfaces = class_implements($className);

return $interfaces && in_array($interface, class_implements($className));
}
}
1 change: 0 additions & 1 deletion tests/Functional/EdifactParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use EdifactParser\EdifactParser;
use EdifactParser\Exception\InvalidFile;

use EdifactParser\Segments\CNTControl;
use EdifactParser\Segments\SegmentInterface;
use EdifactParser\Segments\UNHMessageHeader;
Expand Down
2 changes: 1 addition & 1 deletion tests/Functional/TestingSegmentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class TestingSegmentFactory implements SegmentFactoryInterface
public function __construct(string $customKey)
{
$this->customKey = $customKey;
$this->defaultFactory = new SegmentFactory();
$this->defaultFactory = SegmentFactory::withDefaultSegments();
}

public function segmentFromArray(array $rawArray): SegmentInterface
Expand Down
20 changes: 20 additions & 0 deletions tests/Unit/Segments/NONFakeSegment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace EdifactParser\Tests\Unit\Segments;

/**
* Fake Segment which does not implements the SegmentInterface.
*
* @psalm-immutable
*/
final class NONFakeSegment
{
public array $rawValues;

private function __construct(array $rawValues)
{
$this->rawValues = $rawValues;
}
}
26 changes: 23 additions & 3 deletions tests/Unit/Segments/SegmentFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@
use EdifactParser\Segments\UNHMessageHeader;
use EdifactParser\Segments\UnknownSegment;
use EdifactParser\Segments\UNTMessageFooter;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;

final class SegmentFactoryTest extends TestCase
{
/** @test */
public function segmentValues(): void
public function withDefaultSegments(): void
{
$factory = new SegmentFactory();
$factory = SegmentFactory::withDefaultSegments();

self::assertInstanceOf(UNHMessageHeader::class, $factory->segmentFromArray(['UNH']));
self::assertInstanceOf(DTMDateTimePeriod::class, $factory->segmentFromArray(['DTM']));
self::assertInstanceOf(NADNameAddress::class, $factory->segmentFromArray(['NAD']));
Expand All @@ -30,6 +32,24 @@ public function segmentValues(): void
self::assertInstanceOf(PCIPackageId::class, $factory->segmentFromArray(['PCI']));
self::assertInstanceOf(BGMBeginningOfMessage::class, $factory->segmentFromArray(['BGM']));
self::assertInstanceOf(UNTMessageFooter::class, $factory->segmentFromArray(['UNT']));
self::assertInstanceOf(UnknownSegment::class, $factory->segmentFromArray(['UnknownSegment']));
self::assertInstanceOf(UnknownSegment::class, $factory->segmentFromArray(['___']));
}

/** @test */
public function withCustomSegments(): void
{
$factory = SegmentFactory::withSegments([
'UNH' => UNHMessageHeader::class,
]);

self::assertInstanceOf(UNHMessageHeader::class, $factory->segmentFromArray(['UNH']));
self::assertInstanceOf(UnknownSegment::class, $factory->segmentFromArray(['DTM']));
}

/** @test */
public function exceptionWhenCreatingNonValidTag(): void
{
$this->expectException(InvalidArgumentException::class);
SegmentFactory::withSegments(['NON' => NONFakeSegment::class]);
}
}

0 comments on commit 9a2f5cc

Please sign in to comment.