PHP code example of chemaclass / edifact-parser

1. Go to this page and download the library: Download chemaclass/edifact-parser library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

chemaclass / edifact-parser example snippets


 declare(strict_types=1);

use EdifactParser\EdifactParser;

File('/path/to/order.edi'); // or ->parse($ediString)

foreach ($result->transactionMessages() as $message) {
    echo $message->messageType();      // 'ORDERS', 'INVOIC', 'IFTMIN', …

    // Typed accessors — no magic array indices
    $buyer = $message->segmentByTagAndSubId('NAD', 'BY');
    echo $buyer?->name();              // 'ACME Corporation'
    echo $buyer?->countryCode();       // 'DE'

    foreach ($message->lineItems() as $lineItem) {
        $qty = $lineItem->segmentByTagAndSubId('QTY', '21');
        echo $qty?->quantityAsFloat(); // 100.0
    }
}

$result = EdifactParser::createWithDefaultSegments()->parse($ediString);

$result->transactionMessages();  // list<TransactionMessage> — the UNH…UNT blocks
$result->functionalGroups();     // list<FunctionalGroup>     — UNG…UNE groups, if any
$result->globalSegments();       // TransactionMessage        — file-level UNA/UNB/UNZ

$result->firstMessage();             // ?TransactionMessage
$result->messagesOfType('INVOIC');   // list<TransactionMessage> — an interchange may mix types
count($result);                      // number of messages
foreach ($result as $message) { … }  // iterate the messages directly

$parser = EdifactParser::createWithDefaultSegments();   // NativeTokenizer

use EdifactParser\Tokenizer\SabasTokenizer;

new EdifactParser(SegmentFactory::withDefaultSegments(), tokenizer: new SabasTokenizer());

use EdifactParser\StreamingParser;

foreach (StreamingParser::createWithDefaultSegments()->parseFile('/path/to/large.edi') as $message) {
    process($message); // only one message is held in memory at a time
}

// NAD (Name & Address)
$nad->partyQualifier();  // 'BY'
$nad->name();            // 'ACME Corporation'
$nad->street();
$nad->city();
$nad->postalCode();
$nad->countryCode();     // ISO 3166-1 alpha-2

// QTY / PRI — with numeric conversion
$qty->quantityAsFloat(); // float
$qty->measureUnit();     // 'PCE', 'KGM', …
$pri->priceAsFloat();    // float

// DTM — with date parsing
$dtm->asDateTime();      // DateTimeImmutable|null

$segment->tag();          // 'NAD'
$segment->subId();        // 'BY'
$segment->rawValues();    // ['NAD', 'BY', ['0410106314', '160', 'Z12'], …]

// Fastest single lookup, by tag + subId
$nad = $message->segmentByTagAndSubId('NAD', 'BY'); // ?SegmentInterface

// All segments with a tag (keyed by subId)
$allNad = $message->segmentsByTag('NAD');

$nad?->name(); // always null-check — not every segment exists in every message

// Presence and counts, answered from an index built once per message
$message->has('QTY');       // bool
$message->countByTag();     // ['UNH' => 1, 'NAD' => 2, 'LIN' => 40, …]
count($message);            // total segments, duplicates 

$message->toArray();  // ['type' => 'ORDERS', 'segments' => [['tag' => 'UNH', 'subId' => '1', …], …]]
$message->toJson();   // pretty-printed JSON of the same structure

$segment->toArray();  // ['tag' => 'NAD', 'subId' => 'BY', 'rawValues' => [...]]

// Filter
$message->query()->withTag('NAD')->withSubId('CN')->get();
$message->query()->withTags(['NAD', 'LIN'])->get();
$message->query()->withoutTags(['UNH', 'UNT'])->get();
$message->query()->ofType(NADNameAddress::class)->get();
$message->query()->withTag('PRI')->where(fn($s) => $s->priceAsFloat() > 1000)->get();

// Chain + paginate
$message->query()
    ->withTag('NAD')->withSubId('SU')
    ->where(fn($s) => $s->countryCode() === 'DE')
    ->limit(10)->skip(0)->get();

// Transform / inspect
$message->query()->withTag('NAD')->map(fn($s) => $s->name());
$message->query()->withTag('MOA')->reduce(fn(float $t, $s) => $t + $s->amountAsFloat(), 0.0);
$message->query()->withTags(['QTY', 'PRI'])->groupByTag();  // ['QTY' => [...], 'PRI' => [...]]
$message->query()->countByTag();              // ['NAD' => 2, 'LIN' => 40, …]
$message->query()->withTag('NAD')->first();   // ?SegmentInterface
$message->query()->withTag('NAD')->count();
$message->query()->withTag('UNS')->exists();  // bool

// A query is countable and iterable — no ->get() needed to loop
foreach ($message->query()->withTag('NAD') as $nad) { … }

foreach ($message->lineItems() as $lineItem) {
    $lin = $lineItem->segmentByTagAndSubId('LIN', '1');
    $qty = $lineItem->segmentByTagAndSubId('QTY', '21');

    echo $lin?->itemNumber();      // product identifier
    echo $qty?->quantityAsFloat();

    count($lineItem);                       // segments in this line item
    foreach ($lineItem as $segment) { … }   // …or iterate them
}

foreach ($message->contextSegments() as $context) {
    if ($context->tag() === 'NAD') {
        foreach ($context as $child) {          // …or ->children()
            // $child->tag(), $child->rawValues(), …
        }

        $context->childByTag('CTA');    // ?SegmentInterface — the first one
        $context->childrenByTag('COM'); // list<SegmentInterface> — all of them
        $context->hasChildren();        // bool
        count($context);                // number of children
        $context->toArray();            // the segment with its children nested
    }
}

$buyer = $message->segmentByTagAndSubId('NAD', 'BY'); // NADNameAddress
$buyer?->name();                                       // typed accessors work

$message->childrenOf($buyer);  // list<SegmentInterface> — the CTA/COM under this NAD
$message->contextFor($buyer);  // ?ContextSegment — the same, as a context object

$unb = $result->globalSegments()->segmentByTagAndSubId('UNB', 'UNOC');
$unb?->syntaxIdentifier();            // 'UNOC'
$unb?->senderIdentification();
$unb?->recipientIdentification();
$unb?->preparationDate();             // 'YYMMDD'
$unb?->interchangeControlReference();

$unz = $result->globalSegments()->segmentByTagAndSubId('UNZ', '1');
$unz?->interchangeControlCount();     // number of messages/groups

$unt = $message->query()->withTag('UNT')->first(); // segmentCount(), messageReferenceNumber()
$bgm = $message->query()->withTag('BGM')->first();  // documentCode() e.g. '220', documentNumber()

foreach ($result->functionalGroups() as $group) {
    $group->messageType();               // e.g. 'ORDERS' (from the UNG)
    $group->header()->groupReference();
    $group->trailer()?->controlCount();

    foreach ($group as $message) {  // …or ->messages()
        // …
    }

    count($group);                  // messages in the group
}

use EdifactParser\Analysis\MessageAnalyzer;

$analyzer = new MessageAnalyzer($message);

$analyzer->getType();                     // 'ORDERS'
$analyzer->segmentCount();
$analyzer->lineItemCount();
$analyzer->segmentCountByTag('QTY');
$analyzer->getPartyQualifiers();          // ['BY', 'SU', 'CN'] (unique)
$analyzer->getCurrencies();               // ['EUR']
$analyzer->calculateTotalAmount('125');   // sum MOA with qualifier 125
$analyzer->calculateTotalQuantity('21');  // sum ordered quantities
$analyzer->hasSummarySection();           // UNS present?
$analyzer->getSummary();                  // array of the above

use EdifactParser\Segments\Qualifier\NADQualifier;

$message->query()
    ->withTag('NAD')
    ->where(fn($s) => $s->partyQualifier() === NADQualifier::BUYER) // 'BY'
    ->get();

use EdifactParser\Charset\Charset;

$unb = $result->globalSegments()->segmentByTagAndSubId('UNB', 'UNOC');
$unb?->characterEncoding();                              // 'ISO-8859-1'
$name = Charset::toUtf8($nad->name(), $unb->syntaxIdentifier());

use EdifactParser\Segments\NADNameAddress;
use EdifactParser\Segments\Qualifier\NADQualifier;

$nad = NADNameAddress::builder()
    ->withQualifier(NADQualifier::BUYER)
    ->withPartyId('123456')
    ->withName('ACME Corporation')
    ->withCity('Springfield')
    ->withCountryCode('US')
    ->build();

use EdifactParser\Serializer\EdifactSerializer;
use EdifactParser\Serializer\UnaSeparators;

$serializer = new EdifactSerializer();
echo $serializer->serializeSegment($nad);
// NAD+BY+123456++ACME Corporation++Springfield+++US'

$edi = $serializer->serialize([$unh, $bgm, $nad, $unt], 

use EdifactParser\Writer\InterchangeBuilder;
use EdifactParser\Writer\MessageBuilder;

$edi = InterchangeBuilder::create('SENDER', 'RECIPIENT', 'REF1')
    ->preparedAt('200101', '1200')
    ->addMessage(
        MessageBuilder::create('1', 'ORDERS')
            ->addSegment($bgm)
            ->addSegment($nad)
    )
    ->toString(); // ready-to-send EDIFACT string

use EdifactParser\Validation\MessageRuleSet;
use EdifactParser\Validation\MessageValidator;

$rules = MessageRuleSet::forType('ORDERS')
    -> ->inSequence('UNH', 'BGM', 'UNT');   // relative order of these tags

$validator = new MessageValidator();

foreach ($validator->validate($message, $rules) as $violation) {
    echo "{$violation->segmentTag()}: {$violation->message()}\n";
}

$validator->isValid($message, $rules); // bool

use EdifactParser\Validation\MessageRuleSets;

$validator->validate($message, MessageRuleSets::orders()); // orders(), invoic(), desadv(), iftmin()

namespace YourApp\Segments;

use EdifactParser\Segments\AbstractSegment;

/** @psalm-immutable */
final class EQDEquipmentDetails extends AbstractSegment
{
    public function tag(): string
    {
        return 'EQD';
    }

    // EQD+CN+ABCU1234567+22G1
    public function equipmentQualifier(): string
    {
        return $this->element(1);        // 'CN'
    }

    public function equipmentId(): string
    {
        return $this->firstComponent(2); // 'ABCU1234567'
    }
}

use EdifactParser\EdifactParser;
use EdifactParser\Segments\SegmentFactory;
use YourApp\Segments\EQDEquipmentDetails;

$factory = SegmentFactory::withAdditionalSegments([
    'EQD' => EQDEquipmentDetails::class, // added on top of the 32 built-ins
]);

$parser = new EdifactParser($factory);

$factory = SegmentFactory::withDefaultSegments();

$factory->registeredTags();        // ['BGM', 'CNT', 'COM', … ] — 32 tags, sorted
$factory->classForTag('NAD');      // EdifactParser\Segments\NADNameAddress
$factory->classForTag('ZZZ');      // null — would become an UnknownSegment

$factory->describeTag('QTY')?->accessors();
// ['measureUnit' => 'string', 'qualifier' => 'string',
//  'quantity' => 'string', 'quantityAsFloat' => 'float']

// Envelope structure + just the segments you extract:
$factory = SegmentFactory::withSegments(
    SegmentFactory::ENVELOPE_SEGMENTS + [
        'NAD' => NADNameAddress::class,
        'LIN' => LINLineItem::class,
    ],
);

use EdifactParser\EdifactParser;
use EdifactParser\GroupingRules;
use EdifactParser\Segments\SegmentFactory;
use EdifactParser\StreamingParser;

$rules = GroupingRules::default()
    ->withContextTags(['NAD', 'LIN'])
    ->withChildTags(['CTA', 'COM', 'DTM'])
    ->withBreakLineItemTags(['UNS', 'CNT', 'UNT']);

$parser = new EdifactParser(SegmentFactory::withDefaultSegments(), $rules);

// …or, when the default segments are all you need:
$parser = EdifactParser::createWithDefaultSegments($rules);
$stream = StreamingParser::createWithDefaultSegments($rules);

$segment->toArray(); // ['tag' => 'NAD', 'subId' => 'CN', 'rawValues' => [...]]
$segment->toJson();  // pretty-printed JSON

$message->toArray(); // ['type' => 'ORDERS', 'segments' => [...]] — contexts nested
$message->toJson();

use EdifactParser\Exception\InvalidFile;

try {
    $result = $parser->parseFile('invalid.edi');
} catch (InvalidFile $e) {
    $e->getErrors();   // parser errors, as strings
    $e->getContext();  // extra context, formatted into getMessage()
}

use EdifactParser\Diagnostics\DiagnosticCode;

catch (InvalidFile $e) {
    foreach ($e->getDiagnostics() as $d) {
        $d->code();          // 'segment.unterminated' — stable, match on this
        $d->severity();      // 'error' | 'warning'
        $d->segmentIndex();  // 2
        $d->tag();           // 'NAD'
        $d->elementPath();   // 'C186/6060', when known
        $d->toArray();       // JSON-serialisable
        (string) $d;         // error [segment.unterminated] at segment 2 (NAD): …
    }
}

// The validator speaks the same vocabulary
$diagnostics = (new MessageValidator())->diagnose($message, MessageRuleSets::orders());
$violation->code() === DiagnosticCode::SEGMENT_REQUIRED;