kubinyete / edi-php
A standard library for reading EDI files and declaring your own EDI parsers
Requires (Dev)
- phpunit/phpunit: ^10.3
- symfony/var-dumper: ^6.3
README
A standard library for declaring EDI parsers, because that's not a fun thing to do.
Why should I use it?
This is a very common use-case for many services that output data using a defined text-file layout, for example, many services (Ex: Payment providers) output some sort of layout file that follows a simplified column separated values that has a fixed size set.
For example, the following line (25 bytes):
120240503Hello World
Can be interpreted as
How it works
To provide a better developer experience while creating these parsers, this library provides some useful property attributes and a base class for declaring these types of data.
For example, to declare the previous line, we can declare it in code as the following:
final class ExampleLayoutType extends Registry { #[Number(1)] public int $type; #[Date(8, format: '!Ymd')] public DateTimeInterface $type; #[Text(16)] public string $message; } // We can parse the data directly $example = ExampleLayoutType::from('120240503Hello World ') // Or we can hydrate it $example = new ExampleLayoutType(); $example->hydrate('120240503Hello World ');
Declaring dynamic parsers
Using our built in line parser, we can create dynamically parsed (by line) types, with the power of generators, these types are parsed on demand:
// Using a standard line parser, iterates over each line class EDIParser extends LineParser { // Ran on each line iteration protected function parse(LineContext $ctx): ?Registry { [$contents, $number] = $ctx->unwrap(); // For this EDI file, we can deduce which type it is based on // the first 2 letters provided each line. $code = substr($contents, 0, 2); try { return match ($code) { EDIRegistry::TYPE_HEADER_START => EDIHeader::from($contents), EDIRegistry::TYPE_TRANSACTION_BATCH_START => EDITransactionBatch::from($contents), EDIRegistry::TYPE_SALE_RECEIPT => EDISaleReceipt::from($contents), // Our LineContext can provide a more verbose message to inform // where in our data the error ocurred. default => $ctx->raise("Cannot parse EDI of type '$code'", 0), }; } catch (FieldException $e) { $ctx->raise($e->getMessage(), $e->getCursor()); } } } // Using our parser directly // Reading directly from stdin $buffer = Stream::file('php://stdin', 'rb'); $parser = EDIParser::loadFromStream($buffer); foreach ($parser as $registry) { /** @var Registry $registry */ // We have direct access to our parsed objects on demand dump($registry); }
Understanding parsing errors
This feature is work-in-progress, there are a some things we can do to make it better
We enable out-of-the-box support for friendly contextual messages on any errors that ocurred.
PHP Fatal error: Uncaught Kubinyete\Edi\Parser\Exception\ParseException: Line 1: Failed to parse field '202d1001025840' as a date with format 'YmdHis' Contents: "A00003.1202d1001025840000442ADIQ SOLUCOES PAGAMENTOS S.A 0040002783189N000001" --------^ in /home/vitorkubinyete/code/edi-php/src/Parser/LineContext.php:38