Giter Site home page Giter Site logo

hl7's Introduction

CI Status Total Downloads Latest Stable Version License

Important: Minimum supported PHP version has been updated to 8.0
Last supported versions:
-> PHP 7.0 or 7.1 => 1.5.4
-> PHP 7.2 => 2.0.2
-> PHP 7.4 => 2.1.7

Introduction

A PHP-based HL7 v2.x Parsing, Generation and Sending library, inspired from the famous Perl Net-HL7 package.

Installation

composer require aranyasen/hl7

Usage

Import library

// First, import classes from the library as needed...
use Aranyasen\HL7; // HL7 factory class
use Aranyasen\HL7\Message; // If Message is used
use Aranyasen\HL7\Segment; // If Segment is used
use Aranyasen\HL7\Segments\MSH; // If MSH is used
// ... and so on

Parsing

// Create a Message object from a HL7 string
$message = HL7::from("MSH|^~\\&|1|")->createMessage(); // Returns Message object

// Or, using Message class...
$message = new Message("MSH|^~\\&|1|\rPID|||abcd|\r"); // Either \n or \r can be used as segment endings

// Get string form of the message
echo $message->toString(true);

// Extracting segments and fields from a Message object...
$message->getSegmentByIndex(1); // Get the first segment
$message->getSegmentsByName('ABC'); // Get an array of all 'ABC' segments
$message->getFirstSegmentInstance('ABC'); // Returns the first ABC segment. Same as $message->getSegmentsByName('ABC')[0];

// Check if a segment is present in the message object
$message->hasSegment('ABC'); // return true or false based on whether PID is present in the $message object

// Check if a message is empty
$message = new Message();
$message->isempty(); // Returns true

Composing new messages

// The class `HL7` can be used to build HL7 object. It is a factory class with various helper methods to help build a hl7.
$message = HL7::build()->createMessage(); // Creates an empty message

// The HL7 factory class provides methods that can be chained together in a fluent fashion 
$message = HL7::build()
    ->withComponentSeparator('#')
    ->withFieldSeparator('-')
    ->createMessage();

// Or, using Message class... 
$message = new Message();

Message constructor parameters

// When a message is composed using Message class, there are multiple parameters available to define the properties of the HL7.
// Note: All of these properties are available as fluent methods in HL7 factory class (shown above). So it's recommended to use that for readability

// Creating multiple message objects may have an unexpected side effect: segments start with wrong index values (Check tests/MessageTest for explanation)...
// Use 4th argument as true, or call resetSegmentIndices() on $message object to reset segment indices to 1
$message = new Message("MSH|^~\&|||||||ORM^O01||P|2.3.1|", null, true, true);
// ... any segments added here will now start index from 1, as expected.
// Sometimes you may want to have exact index values, rather than auto-incrementing for each instance of a segment
// Use 5th argument as false...
$hl7String = "MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n";
$message = new Message($hl7String, null, true, true, false); $// $message contains both OBXs with given indexes in the string
// Create a segment with empty sub-fields retained
$message = new Message("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|", null, true); // Third argument 'true' forces to keep all sub-fields
$pv1 = $message->getSegmentByIndex(1);
$fields = $pv1->getField(3); // $fields is ['', 'AAAA1', '', '', 'BB']

// Create/send message with segment-ending bar character (|) removed
$message = new Message("MSH|^~\\&|1|\nABC|||xxx\n", ['SEGMENT_ENDING_BAR' => false]);
$message->toString(true); // Returns "MSH|^~\&|1\nABC|||xxx\n"
(new Connection($ip, $port))->send($message); // Sends the message without ending bar-characters (details on Connection below) 

// Specify custom values for separators, HL7 version etc.
$message = new Message("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|", ['SEGMENT_SEPARATOR' => '\r\n', 'HL7_VERSION' => '2.3']);

// Segment with separator character (~) creates sub-arrays containing each sub-segment
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1"); // Creates [[3,0], [4,1]]
        
// To create a single array instead, pass 'true' as 6th argument. This may be used to retain behavior from previous releases
// Notice: Since this leads to a non-standard behavior, it may be removed in future
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1", null, false, false, true, true); // Creates ['3', '0~4', '1']
// or
$message = new Message("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1", doNotSplitRepetition: true); // Creates ['3', '0~4', '1']

Handling segments and fields

// Once a message object is created, we can now add, insert, set segments and fields.

// Create a MSH segment and add to message object
$msh = new MSH();
$message->addSegment($msh); // Message is: "MSH|^~\&|||||20171116140058|||2017111614005840157||2.3|\n"

// Create a custom segment
$abc = new Segment('ABC');
$abc->setField(1, 'xyz');
$abc->setField(2, 0);
$abc->setField(4, ['']); // Set an empty field at 4th position. 2nd and 3rd positions will be automatically set to empty
$abc->clearField(2); // Clear the value from field 2
$message->setSegment($abc, 1); // Message is now: "MSH|^~\&|||||20171116140058|||2017111614005840157||2.3|\nABC|xyz|\n"

// Create a defined segment (To know which segments are defined in this package, look into Segments/ directory)
// Advantages of defined segments over custom ones (shown above) are 1) Helpful setter methods, 2) Auto-incrementing segment index 
$pid = new PID(); // Automatically creates PID segment, and adds segment index at PID.1
$pid->setPatientName([$lastname, $firstname, $middlename, $suffix]); // Use a setter method to add patient's name at standard position (PID.5)
$pid->setField('abcd', 5); // Apart from standard setter methods, you can manually set a value at any position too
unset($pid); // Destroy the segment and decrement the id number. Useful when you want to discard a segment.

Send messages to remote listeners

Side note: In order to run Connection you need to install PHP ext-sockets https://www.php.net/manual/en/sockets.installation.php

$ip = '127.0.0.1'; // An IP
$port = '12001'; // And Port where a HL7 listener is listening
$message = new Message($hl7String); // Create a Message object from your HL7 string

// Create a Socket and get ready to send message. Optionally add timeout in seconds as 3rd argument (default: 10 sec)
$connection = new Connection($ip, $port);
$response = $connection->send($message); // Send to the listener, and get a response back
echo $response->toString(true); // Prints ACK from the listener

ACK

Handle ACK message returned from a remote HL7 listener...

$ack = (new Connection($ip, $port))->send($message); // Send a HL7 to remote listener
$returnString = $ack->toString(true);
if (strpos($returnString, 'MSH') === false) {
    echo "Failed to send HL7 to 'IP' => $ip, 'Port' => $port";
}
$msa = $ack->getFirstSegmentInstance('MSA');
$ackCode = $msa->getAcknowledgementCode();
if ($ackCode[1] === 'A') {
    echo "Received ACK from remote\n";
}
else {
    echo "Received NACK from remote\n";
    echo "Error text: " . $msa->getTextMessage();
}

Create an ACK response from a given HL7 message:

$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
$ackResponse = new ACK($msg);

Options can be passed while creating ACK object:

$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
$ackResponse = new ACK($msg, null, ['SEGMENT_SEPARATOR' => '\r\n', 'HL7_VERSION' => '2.5']);

APIs

This package exposes a number of public methods for convenient HL7 handling. Some examples are:

  1. Considering you have a Message object (say, $msg = new Message(file_get_contents('somefile.hl7'));)
    $msg->toFile('/path/to/some.hl7'); // Write to a file
    $msg->isOru(); // Check if it's an ORU
    $msg->isOrm(); // Check if it's an ORM

Visit docs\README for details on available APIs

All segment level getter/setter APIs can be used in two ways -

  • If a position index isn't provided as argument (1st argument for getters, 2nd for setters), a standard index is used.
    $pid->setPatientName('John Doe') -> Set patient name at position 5 as per HL7 v2.3 standard
    $pid->getPatientAddress() -> Get patient address from standard 11th position

  • To use a custom position index, provide it in the argument:
    $pid->setPatientName('John Doe', 6) -> Set patient name at 6th position in PID segment
    $pid->getPatientAddress(12) -> Get patient address from 12th position

Issues

Bug reports and feature requests can be submitted on the Github Issue Tracker.

Contributing

See CONTRIBUTING.md for information.

hl7's People

Contributors

ajibarra avatar arnowelzel avatar damienharper avatar durandsacha avatar fernando-rivas-smtp avatar henry11996 avatar isangil avatar lampi87 avatar luilliarcec avatar maxence-machu avatar mmonahanfl avatar peter279k avatar pluk77 avatar samuel-chane avatar senaranya avatar silvioq avatar svenvanhees avatar tuxoff avatar tysonlist avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hl7's Issues

Authentication in headers

Hi i'm probably missing something simple here but i can not set Auth headers for my connection. I don't see it in your docs either could you point me in the right direction please.

Since Version 2.0.1 Messages are added an additional level

Up to the version 2.0.1 a new nested level is added to readed messages.

For example a PID Messages like:

MSH|^~\&|||||||ADT^A01||P|2.3.1| PID|1||6255197

The PID Segment in version 2.0.0 has been interpreted as follows:
<PID.1><PID.1.1>1</PID.1.1></PID.1><PID.2/><PID.3><PID.3.1>6255197</PID.3.1><PID.3.2/><PID.3.3/><PID.3.4/><PID.3.5>IDSGPA</PID.3.5></PID.3><PID.3><PID.3.1>6255197</PID.3.1></PID.3></PID>

Up to the version 2.0.1 the message is interpreted as follows:
<PID><PID.1><PID.1.1>1</PID.1.1></PID.1><PID.2/><PID.3><PID.3.1><PID.3.1.1>6255197</PID.3.1.1></PID.3.1></PID>

I think the new nested segments are wrong and shall be corrected and display as in the version 2.0.0.

Issues connecting to a server

I've been using your library for integrating our services with a few other service providers, and it's been working wonderfully – except for one provider.

Sending them a message results in a HL7ConnectionException with message 'Response partially received. Timed out listening for end-of-message from server error. The message itself is sent, but I just can't get the response. If I relay the message through Mirth Connect (i.e., I set up a listener on Mirth which, upon receiving a message, sends it to the service provider) it works without any issues, and the ACK is properly returned.

I tried changing the response encoding (it's ISO-8859-1) and it didn't make any difference. Increasing the timeout to 2 minutes didn't change anything either, and through Mirth the response is immediate. Debugging $data and the socket status just before the exception is thrown shows that $data is empty and the socket had no errors (i.e., socket_last_error() === 0).

Using Mirth as a proxy works, but it's an extra point of failure and, since I'm still fairly new to this, I'm having trouble configuring it to return the ACK message, so I would love it if I could find a way to make this library work directly.

Important Question

Because when instantiating the creation of a message and sending it, the hl7 server responds with ack: list index out of limits 3, as if the message was not separating the segments with \n

toString method remove spaces between sections

This error was discovered when i create a new message using the method " new Message",
creating a new object from a string hl7 message format and next sending this message using a new connection using connection->send(Message).

Example :
$msjSend =new Message("MSH|^&|HL7Soup|Instance1|||||ADT^A04|1817457|D|2.5.1|123456||AL
PID||0797675^^^^MR|454721||Brown^Sam^^^^^B|Smith^Mary^^^^|19780203|M||2106-3|254 East St^^Howick^OH^3252^USA||(216)671-4859|||S|AGN|400003603
1629086|999-8888|
NK1||Brown^Mary^^^^|MTH||(216)891-3459||EC|||||||||||||||||||||||||||
PV1||O|O/R|A|||060277^Allen^Katrina^J^^^|||||||||| ||2668684|||||||||||||||||||||||||201407271408|");
$connection = new Connection($ip,$port);
$response = $connection->send($msjSend);

Receive:
"MSH|^&|HL7Soup|Instance1|ADT^A04|1817457|D|2.5.1|123456|AL
PID|0797675^MR|454721|Brown^Sam^B|Smith^Mary|19780203|M|21060101000000-3|254 East St^^Howick^OH^3252^USA||(216)671-4859|S|AGN|400003603
1629086|999-8888
NK1|Brown^Mary^|MTH|(216)891-3459|EC
PV1|O|O/R|A|060277^Allen^Katrina^J^|2668684|201407271408"

Contructing a segment but not adding it in message leaves a gap in index numbering

To reproduce: Create a segment:

$pid1 = new PID();

Do not add this to HL7 message (there can be reasons - e.g., we may decide not to add if the segment size isn't greater than 1).

Now create another segment:

$pid2 = new PID();

Add this one:

$hl7Msg->addSegment($pid2);

Expected message:

MSH|........|
PID|1|....|

Received Message:

MSH|........|
PID|2|....|

That is, it leaves a gap in place of the un-added segment.
This happens as we use a static variable to count serial index.

Socket validation issue on PHP 8

Hello,

I was getting the following error when attempting to create a new Connection on PHP 8:

Aranyasen\Exceptions\HL7ConnectionException with message 'Failed to create socket: Success'

After looking into it, I've found that it's because sockets have been moved from resources to objects, so the if on Connection:72 has to be changed from:

if (!$socket || !is_resource($socket))

To:

if (!$socket || !(is_resource($socket) || $socket instanceof \Socket))

I'd submit a PR myself, but to be honest I've never done it before and I'm not sure I'd do it correctly.

Best regards!

Edit: according to https://php.watch/versions/8.0/sockets-sockets-addressinfo changing is_resource($socket) to $socket !== false also works, so I guess changing the if statement to just if (!$socket) would also work…?

Repetition separator not implemented

Repeatable fields in a HL7 file, like PID3, aren't handled:
image

For example, in a file that would look like this:

MSH|^~\&|||||||ADT^A01||P|2.3.1|
PID|||3^0~4^1

the result in PID3 should be:

[3] => Array(
          [0] => Array(
                    [0] => 3
                    [1] => 0
                    )
          [1] => Array(
                    [0] => 4
                    [1] => 1
                    )
          )

How to run the code

I have install all thing as said but doesn't run, can you make running code with example

toString on pid 5.3 error exception

When using toString() on a message that has the pid 5.1 field filled in, the segmentToString function returns an ErrorException: Array to string conversion.

Reproducible by using a pid 5.1 like this:

Test &""&firstname &""&""^F^N N^^^^F^^^^""~Test &""&Test^Test^""^^^^B~""&""&""^^^^^^P~^Lastname^^^^^N

The implode function in segmentToString tries to implode the pid 5 field, which looks like this:

array:5 [
  0 => array:5 [
    0 => "Test "
    1 => """"
    2 => "firstname "
    3 => """"
    4 => """"
  ]
  1 => "F"
  2 => "N N"
  3 => "F"
  4 => """"
]

index of row

Hi, i'm not so familiar with HL7 so i dont know how to pronounce it correctly. But i have problem that i send one message and on other side i get some number (index number). So example is on atteched image. As you can see i get in this example number 1 but on sended message is not included.

Sended message:
Posnetek

and recived message you can see here:
Posnetek zaslona 2022-02-12 005248

No response received within 10 seconds

Hello guys, I have the following error, I did not receive a response in 10 seconds, when I change the timeout parameters, the same message appears with the established seconds, could you help me solve it, I already did tests from another application and it works correctly Connection.

I hope prompt response thanks.

JT

Wrong segment index values when multiple messages are created

The segment classes use static properties to maintain segment-index. This is required as one can add new segments to a given message object and expect the index to auto-increment. The side-effect to this is, if you create a second message, adding the same segments will now start indexing from their index in the last message instead of 1! This happens because static properties are class properties, not object ones, and thus shared across all objects.

To counter this, add a 4th argument in message constructor, and expose a public method resetSegmentIndices(). Users should be able to use one of these to reset segment indices to 1.

Repetition

Hi,
how about adding this code to Message.php on line 120 to get repetition parsed?

if(stripos($fields[$j],$this->repetitionSeparator)==false) {
$comps = preg_split("/\" . $this->componentSeparator . '/', $fields[$j], -1, $preg_flags);
foreach ($comps as $k => $kValue) {
$subComps = preg_split("/\" . $this->subcomponentSeparator . '/', $comps[$k]);
(\count($subComps) === 1) ? ($comps[$k] = $subComps[0]) : ($comps[$k] = $subComps);
}
(\count($comps) === 1) ? ($fields[$j] = $comps[0]) : ($fields[$j] = $comps);
} else {
$repets = preg_split("/\" . $this->repetitionSeparator . '/', $fields[$j], -1, 0);
$compsr=[];
foreach ($repets as $rValue) {
$comps = preg_split("/\" . $this->componentSeparator . '/', $rValue, -1, $preg_flags);
foreach ($comps as $k => $kValue) {
$subComps = preg_split("/\" . $this->subcomponentSeparator . '/', $comps[$k]);
(\count($subComps) === 1) ? ($comps[$k] = $subComps[0]) : ($comps[$k] = $subComps);
}
(\count($comps) === 1) ? ($compsr[] = $comps[0]) : ($compsr[] = $comps);
}
$fields[$j] = $compsr;
}

AIG Segment parameter inversion

there is a parameter inversion in the setFields of the AIG segment

public function setID(int $value, int $position = 1): bool
    {
        return $this->setField($position, $value);
    }

    public function setSegmentActionCode($value, int $position = 2): bool
    {
        return $this->setField($value, $position);
    }

$value is inversed with $position generating errors
Aranyasen\HL7\Segment::setField(): Argument #1 ($index) must be of type int, string given, called in /var/www/html/api-6/vendor/aranyasen/hl7/src/HL7/Segments/AIG.php on line 64

Not a valid message: invalid control segment

ACK response received contain hexadecimal characters that are not deleted prior to the construction of the message, causing this error.

Screenshot from 2020-06-30 08-52-52

This ACK response has been generated by Nextgen connect 3.9 with the MLLPV2 protocol.

Screenshot from 2020-06-30 08-58-38

one possible solution has been to replace the hexadecimal characters right after the reading of the buffer.

Screenshot from 2020-06-30 09-01-58

  • line 136

Call to undefined method Aranyasen\HL7::from()

$message = HL7::from($file_data)->createMessage();

i am trying to create a Message, getting the error below

Call to undefined method Aranyasen\HL7::from()

laravel 9.52
PHP 8.2
"aranyasen/hl7": "^2.1"

PHP version

I see the php version requirements have been updated and 7.2 has been removed. What is the reason for this, as it looks like there does not seem to be code that is not able to run on 7.2.

Would you be open to adding a new branch / version that continues with PHP 7.2 where patches can be ported to keep those using your library with 7.2 at least up to date with bug fixes?

I need help

I already install from composer, and I try to test, but i get this error in console:
crbug/1173575, non-JS module files deprecated.

My php versión is 7.3.24

this is my code:
`<?php

// First, import classes from the library as needed...
use Aranyasen\HL7\Message; // If Message is used
use Aranyasen\HL7\Segment; // If Segment is used
use Aranyasen\HL7\Segments\MSH; // If MSH is used
// ... and so on

// Create a Message object from a HL7 string
$msg = new Message("MSH|^~\&|1|\rPID|||abcd|\r"); // Either \n or \r can be used as segment endings
$pid = $msg->getSegmentByIndex(1);
echo $pid->getField(3); // prints 'abcd'
echo $msg->toString(true); // Prints entire HL7 string

// Get the first segment
$msg->getFirstSegmentInstance('PID'); // Returns the first PID segment. Same as $msg->getSegmentsByName('PID')[0];

// Check if a segment is present in the message object
$msg->hasSegment('PID'); // return true or false based on whether PID is present in the $msg object

// Check if a message is empty
$msg = new Message();
$msg->isempty(); // Returns true

?>
`

Thanks!

Problem correlating notes to OBXs

I am processing laboratory test results. Each OBX statement may have an NTE segment that contains details I need to correlate to the OBX segment when it exists. Unfortunately, the NTE index resets to 1 after each OBX like this:

MSH|^~\&|IM||LIS|ABCD|20200522122114||ORU^R01|49071956|P|2.4|||AL|NE
PID|1||||||||||^^^^^||||||||
PV1|1||^^^^^^|||||||||||||||||
ORC|RE|W1234567890000|||||^^^^^R|||||
OBR|1|W1234567890000|||R||||^||||||5^^^^^^P|||3601309|^^|8^1^|||||||^^^^^R|||||||KLA||||||||||
OBX|1|TX|TST1||See Replicates|||Hef0~Ief0~Tef0~ecMN~ef0|||V|||20200522113231||KLA||36001309|20200522122033
OBX|2|TX|TST2||Non-reactive|||Hef0~Ief0~Tef0~efQ|||V|||20200522113235||KLA||36001309|20200522121917
NTE|1|L|0.01
OBX|3|TX|TST3||Non-reactive|||Hef0~Ief0~Tef0~efQ|||V|||20200522113254||KLA||36001309|20200522121936
NTE|1|L|0.01
OBX|4|TX|TST3||Non-reactive|||Hef0~Ief0~Tef0~efQ|||V|||20200522113313||KLA||36001309|20200522121955
NTE|1|L|0.01
OBX|5|TX|TST3||Non-reactive|||Hef0~Ief0~Tef0~efQ|||V|||20200522113332||KLA||36001309|20200522122014
NTE|1|L|0.01
OBX|6|TX|TST4||Non-reactive|||Hef0~Ief0~Tef0~efQ|||V|||20200522113351||KLA||36001309|20200522122033
NTE|1|L|0.01
OBX|7|NM|950||15|||ef5|||V|||20200522113231||KLA||36001309|20200522113405
OBX|8|NM|951||3|||ef0|||V|||20200522113231||KLA||36001309|20200522113405
OBX|9|NM|952||20|||ef5|||V|||20200522113231||KLA||36001309|20200522113405

I have seen similar behavior from another vendor for the SSU message

MSH|^~\&|IM||LIS|ABCD|20200116165227||SSU^U03|3390413|P|2.4|||AL|NE
EQU|p123|20200116165227
SAC|||=W1234567890000||2||20200116163903||ARCH_S|4ORT0116_2|A^1|||||||||||0000
OBX|1||^^^ARCHIVE
SAC|||=W1234567890001||2||20200116163909||ARCH_S|4ORT0116_2|B^1|||||||||||0000
SAC|||=W1234567890002||2||20200116163911||ARCH_S|4ORT0116_2|C^1|||||||||||0000
OBX|1||^^^ARCHIVE

Please note that I am not in control of the generation of the messages, I just need to be able to process them. Ideally, the HL7 package would be able to detect 'child' segments and do more direct correlation, but I have not seen where the HL7 spec makes it clear as to which segments refer to a prior segment. It may be that every time a segment index is one, it and all the same segments until reset are logically the children of the previous segment, but I do not have enough examples of vendor output to feel comfortable suggesting that type of overhaul.

As a first step, could we consider adding the current message_index to the Segment constructor? With that information, users can add special code as needed to use that information to correlate fields. Since it is a non-trivial change, I'd like to hear other opinions on the best way to save the information we need to do correlation when this type of behavior occurs.
...

I woke up in the middle of the night realizing I was biased by the way my code is currently going through the message. I am using getSegmentsByName and I can switch to getSegments, It is a pretty brutal refactor. I still think having some way to determine where a segment is in a message from the segment object would be useful, but if you don't want to go that way there is an alternative in the current code.
....

Ok, just found getSegmentIndex... I originally thought this was the index within the segment but it looks like it may be what I need. Cancelling.

Run without Composer

can you describe how to use this library without composer, but with autoload?

Thanks

Segment separators are not correct

In certain places the segment separators are defined as following:

$this->hl7Globals['SEGMENT_SEPARATOR'] = '\n';

However - this is not correct:

  1. The sequence '\n' will translate to two characters and not to a newline.
  2. The separator must be "\015" (ASCII 13, CR) and not just a newline.

inconsistent data position when parsing information

I am working with HL7v2.5.1 and new to HL7.
It might be intended for the format but the data index is inconsistent depending on message data.

When receiving an ADT^A04 event for example, the patient firstname, email and phone number are optional.
I might receive:
PID|||25577203^^^App^PI||LASTNAME^^^^^^L|||U|MaidenName^^^^^^M||||
PID|||25577202^^^App^PI||LASTNAME^FirstName^^^^^L||19890220|M|Maiden_name^^^^^^M||||^^^[email protected]
PID|||25577185^^^App^PI||LASTNAME^FirstName^^^^^L|||F|||||~+33320459845
PID|||25576798^^^App^PI||LASTNAME^FirstName^^^^^L||19890101|M|Maiden_name^^^^^^M||street name^^City^^00000||^^^[email protected]~+33212358226
PID|||25577698^^^App^PI||LASTNAME^FirstName^^^^^L||19890101|M|Maiden_name^^^^^^M||||+33607252627^^^[email protected]~+33405263633

When parsing the data using the Message constructor, pid->getField(5) //getPatientName will return an array
with [0=>LASTNAME, 1=>L] or [0=>LASTNAME, 1=>FirstName, 3=>L]

It is worse for the phone number & email.
$pid->get(13) can return a simple string like the email or phone number, an array
[
0 => "[email protected]"
1 => "+33212358226"
]
or
[
0 => "+33320459845"
]
or even an array of array and string
[
0 => array:2 [
0 => "+33607252627"
1 => "[email protected]"
]
1 => "+33405263633"
]

When I look at the HL7v2.5.1 PID13 definition

PID.13.1 - Telephone Number
PID.13.2 - Telecommunication Use Code
PID.13.3 - Telecommunication Equipment Type
PID.13.4 - Email Address
PID.13.5 - Country Code
PID.13.6 - Area/City Code

I would expect an array [
0 => "phoneNumber",
1=>"",
...
3=>"email"
...;
]

Same for the name:
[
0 => "LASTNAME",
1=>"FirstName",
...
6=>"NameTypeCode"
...;
]

Am I not using the correct settings or misunderstanding the library usage?

Creating Segments with repetition data

Hello, first of all, thanks for a great library!
I have a question regarding Segment Field Repetition. Is it possible to create hl7 messages using your library with a repetition in some fields? Like this:

$contactData = [
    [
        0 => 'some phone number',
        1 => 'PH',
        2 => 'type_phone',
    ],
    [
        0 => 'some mobile phone',
        1 => 'PH',
        2 => 'type_mobile',
    ],
    [
        0 => 'email',
        1 => 'PH',
        2 => 'type_email',
    ],
]
$segmentPID->setPhoneNumberHome($contactData);

So, in the end, when I'm building a message, I would get this string:

PID|||||||||||||some phone number^PH^type_phone~some mobile phone^PH^type_mobile~email^PH^type_email

Is it possible? Thanks in advance for the answer

Collect the third level

Hello,

Can we easily get the third level of an object?

Example :
$orc->getEnteringOrganization()->getAlternateIdentifier() ?

Because I feel like it's not possible.

^^^123456

So how to recover ORC17.4 because $orc->getEnteringOrganization() returns a string.

Thanks you!

Issue parsing some valid HL7 message strings

Below is a well-formed HL7 ORU message.

$oruMessageString = "MSH|^~\&|HL7Soup|Instance1|HL7Soup|Instance2|20060922162830|L674-200609221628310220|ORU^R01|ORU000016168|P|2.3|||AL|AL
        PID||75675|1478895^4^M10^PA||XTEST^PATIENT^||19591123|M||||||||||||||||||||||
        ORC|RE|F4334|51013174200601|||||^|||||||||||
        OBR|1|F4334|51013174200601|80048^BASIC METABOLIC PANEL|||20060922152300||||||||^^^^^|023901^PACLAB| ||||||^|CH|F|^^|^^^20060922162659^^GHA||^|||^^^^^^ ^^^^|^^^^^^^^^^|^^^^^^^^^^|^^^^^^^^^^||||||||
        OBX|1|NM|84295^SODIUM^GH|1|145|mmol/L|||||F|||20060922152300|GH";  

I have tried parsing the message as follows:

$oruMessage = new \Aranyasen\HL7\Message($oruMessageString);  

But I get an exception with the following message:

// Segment name ' PID' should be 3 characters and in uppercase  

I have traced the exception to the following line:

if ((!$name) || (\strlen($name) !== 3) || (strtoupper($name) !== $name)) {

I have checked the $oruMessageString to ensure there are no unnecessary space characters. I have also validated against other HL7 tools like HL7 Soup to ensure that $oruMessageString is a valid HL7 message string.

I believe that trimming whitespaces around $name in the Segment constructor before

if ((!$name) || (\strlen($name) !== 3) || (strtoupper($name) !== $name)) {
is called will sort out this issue.
If this is okay, I will go ahead and submit a pull request with the fix.
Please advice.
Thank you.

Receive multiple answers

Hi,

We are dealing with a HL7 connection where after we send a message the server returns multiple messages, but the library only process the first one. Is it there any option to wait until the server has sent all the messages?

Thank you!!

Creating an OBX.5 field with ED data type breaks on toString

Greetings!

I've been using the library to parse ORUs and ORMs, and have found out that when creating an OBX segment using the ED data type for OBX.5, it tries to implode() the segment array, but as ED.1 is also a complex data type (therefore an array), the implode fails causing an array to string conversion exception to be thrown.

Here's how the OBX structure looks like.
Screenshot 2023-08-10 at 17 33 46

From what I've gathered looking arround the conversion code, the implode happens at HL7/Message.php:336, and doesn't seem to support an complex data type inside a repetition.

Am I doing something wrong or should I work on a fix for this?

Using constants for field positions

Would you be open to receiving a PR for adding constants to the segments (and perhaps drop the $position argument completely)? Hard-coding the same value in setter and getters seems a bit odd and by using constants you have additional benefits in your IDE and documentation.

For simple segments, one could even completely bypass the getter and setters without the risk of using the wrong position:

$orc = new ORC();
$orc->setField(ORC::ORDER_CONTROL, $value);

I propose to change the segments like this:

class ORC extends Segment
{
    const ORDER_CONTROL = 1;
    const PLACE_ORDER_NUMBER = 2;
    
    public function __construct(array $fields = null)
    {
        parent::__construct('ORC', $fields);
    }

    public function setOrderControl($value)
    {
        return $this->setField(self::ORDER_CONTROL, $value);
    }

    public function setPlacerOrderNumber($value)
    {
        return $this->setField(self::PLACE_ORDER_NUMBER, $value);
    }

    public function getOrderControl()
    {
        return $this->getField(self::ORDER_CONTROL);
    }

    public function getPlacerOrderNumber()
    {
        return $this->getField(self::PLACE_ORDER_NUMBER);
    }
}

or like this

class ORC extends Segment
{
    const ORDER_CONTROL = 1;
    const PLACE_ORDER_NUMBER = 2;
    
    public function __construct(array $fields = null)
    {
        parent::__construct('ORC', $fields);
    }

    public function setOrderControl($value, int $position = self::ORDER_CONTROL)
    {
        return $this->setField($position, $value);
    }

    public function setPlacerOrderNumber($value, int $position = self::PLACE_ORDER_NUMBER)
    {
        return $this->setField($position, $value);
    }

    public function getOrderControl(int $position = self::ORDER_CONTROL)
    {
        return $this->getField($position);
    }

    public function getPlacerOrderNumber(int $position = self::PLACE_ORDER_NUMBER)
    {
        return $this->getField($position);
    }
}

Array to string conversion when processing ADT message

Processing the following message (and calling toString()) throws Array to string conversion in src/HL7/Message.php:388:

MSH|^~\&|SAC|PIVA|DIP_SAC|Dipartimentale|20190531204652||ADT^A40|0d9834c0-6efa-4153-aa10-e349017057c9|P|2.5
EVN||20190531204652
PID|||D298B31F-53E2-4FCD-9246-240B61792023^55^^PK~9143224^^^ABC|| COGNOME^NOME||19310525|F|||&VIA   MEUCCI 3^^020066^^46019^^H~&VIA   MEUCCI 3^^020066^^46019^^L~&^^020035^^^^BDL||^^PH~^^CP|||||CF|7289721||||020035|||||100^Italia
MRG|936815CB-75C2-45BD-BBB7-F5BEACED3D63

Enhancement : ROL segment

Hi !
Here is a suggestion for the ROL segment.
No much time to make a pull request, sorry for that.

<?php

namespace Aranyasen\HL7\Segments;

use Aranyasen\HL7\Segment;

/**
 * ROL: Role Segment
 * Ref: https://hl7-definition.caristix.com/v2/HL7v2.5.1/Segments/ROL
 */
class ROL extends Segment
{
    public function __construct(array $fields = null)
    {
        parent::__construct('ROL', $fields);
    }

    public function setID($value, int $position = 1)
    {
        return $this->setField($position, $value);
    }

    public function setActionCode($value, int $position = 2)
    {
        return $this->setField($position, $value);
    }

    // See: https://hl7-definition.caristix.com/v2/HL7v2.5.1/Fields/ROL.3
    public function setRole($value, int $position = 3)
    {
        return $this->setField($position, $value);
    }

    public function setRolePerson($value, int $position = 4)
    {
        return $this->setField($position, $value);
    }

    public function setRoleBeginDateTime($value, int $position = 5)
    {
        return $this->setField($position, $value);
    }

    public function setRoleEndDateTime($value, int $position = 6)
    {
        return $this->setField($position, $value);
    }

    public function setRoleDuration($value, int $position = 7)
    {
        return $this->setField($position, $value);
    }

    public function setRoleActionReason($value, int $position = 8)
    {
        return $this->setField($position, $value);
    }

    public function setProviderType($value, int $position = 9)
    {
        return $this->setField($position, $value);
    }

    public function setOrganizationUnitType($value, int $position = 10)
    {
        return $this->setField($position, $value);
    }

    public function setOfficeHomeAddress($value, int $position = 11)
    {
        return $this->setField($position, $value);
    }

    public function setPhone($value, int $position = 12)
    {
        return $this->setField($position, $value);
    }

    // -------------------- Getter Methods ------------------------------

    public function getID(int $position = 1)
    {
        return $this->getField($position);
    }

    public function getActionCode(int $position = 2)
    {
        return $this->getField($position);
    }

    public function getRole(int $position = 3)
    {
        return $this->getField($position);
    }

    public function getRolePerson(int $position = 4)
    {
        return $this->getField($position);
    }

    public function getRoleBeginDateTime(int $position = 5)
    {
        return $this->getField($position);
    }

    public function getRoleEndDateTime(int $position = 6)
    {
        return $this->getField($position);
    }

    public function getRoleDuration(int $position = 7)
    {
        return $this->getField($position);
    }

    public function getRoleActionReason(int $position = 8)
    {
        return $this->getField($position);
    }

    public function getProviderType(int $position = 9)
    {
        return $this->getField($position);
    }

    public function getOrganizationUnitType(int $position = 10)
    {
        return $this->getField($position);
    }

    public function getOfficeHomeAddress(int $position = 11)
    {
        return $this->getField($position);
    }

    public function getPhone(int $position = 12)
    {
        return $this->getField($position);
    }
}

Support (or ideas) on receiving HL7 from a remote host?

Hi all. This package works great for creating and sending stuff to a remote MLLP server, but is there any plan for adding support (or any ideas) for receiving HL7 data via a "always on" TCP stream?

I've tried using socat and other approaches, but I've not yet arrived at the right solution.

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.