Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kubinyete/edi-php

A standard library for declaring EDI parsers
https://github.com/kubinyete/edi-php

edi library parsing php php8

Last synced: 3 days ago
JSON representation

A standard library for declaring EDI parsers

Awesome Lists containing this project

README

        

# EDI PHP

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

| Range | Size | Column | Value
| ------ | ---- | ---------- | -----
| 0 - 0 | 1 | Reg. type | 1
| 1 - 8 | 8 | Date | 2024-05-03
| 9 - 25 | 16 | Message | Hello World

### 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:

```php
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:

```php
// 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.

```js
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