Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.x #104

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft

4.x #104

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/main_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: tests

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

Expand All @@ -11,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ '8.0', '8.1', '8.2' ]
php-versions: [ '8.2', '8.3', '8.4' ]
name: PHP ${{ matrix.php-versions }} Test on ubuntu-latest
steps:
- name: Checkout
Expand Down
58 changes: 42 additions & 16 deletions .phpdoc-md
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
<?php

use Aranyasen\HL7;
use Aranyasen\HL7\Segments\PV1;
use Aranyasen\HL7\Segments\ORC;
use Aranyasen\HL7\Segments\OBX;
use Aranyasen\HL7\Segments\OBR;
use Aranyasen\HL7\Segments\AIG;
use Aranyasen\HL7\Segments\AIL;
use Aranyasen\HL7\Segments\AIP;
use Aranyasen\HL7\Segments\DG1;
use Aranyasen\HL7\Segments\NTE;
use Aranyasen\HL7\Segments\IN3;
use Aranyasen\HL7\Segments\EQU;
use Aranyasen\HL7\Segments\EVN;
use Aranyasen\HL7\Segments\FHS;
use Aranyasen\HL7\Segments\FTS;
use Aranyasen\HL7\Segments\GT1;
use Aranyasen\HL7\Segments\IN1;
use Aranyasen\HL7\Segments\PID;
use Aranyasen\HL7\Segments\IN3;
use Aranyasen\HL7\Segments\MRG;
use Aranyasen\HL7\Segments\MSA;
use Aranyasen\HL7\Segments\MSH;
use Aranyasen\HL7\Segments\EVN;
use Aranyasen\HL7\Segments\EQU;
use Aranyasen\HL7\Segments\NK1;
use Aranyasen\HL7\Segments\NTE;
use Aranyasen\HL7\Segments\OBR;
use Aranyasen\HL7\Segments\OBX;
use Aranyasen\HL7\Segments\ORC;
use Aranyasen\HL7\Segments\PID;
use Aranyasen\HL7\Segments\PV1;
use Aranyasen\HL7\Segments\PV2;
use Aranyasen\HL7\Segments\RGS;
use Aranyasen\HL7\Segments\SAC;
use Aranyasen\HL7\Segments\SCH;
use Aranyasen\HL7\Segments\TQ1;
use Aranyasen\HL7\Segment;
use Aranyasen\HL7\Messages\ACK;
use Aranyasen\HL7\Message;
Expand All @@ -24,22 +37,35 @@ return (object)[
'destDirectory' => 'docs',
'format' => 'github',
'classes' => [
Connection::class,
Message::class,
ACK::class,
Segment::class,
MSH::class,
PID::class,
ACK::class,
Connection::class,
AIG::class,
AIL::class,
AIP::class,
DG1::class,
EQU::class,
EVN::class,
FHS::class,
FTS::class,
GT1::class,
IN1::class,
IN3::class,
MRG::class,
MSA::class,
MSH::class,
NK1::class,
NTE::class,
DG1::class,
OBR::class,
OBX::class,
ORC::class,
PID::class,
PV1::class,
EVN::class,
EQU::class,
PV2::class,
RGS::class,
SAC::class,
SCH::class,
TQ1::class,
],
];
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2017 Aranya Sen
Copyright (c) 2017-2023 Aranya Sen

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
102 changes: 50 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
</p>

## Important
- "_**new Message()**_" is deprecated and could be removed in a future release. Use _**HL7**_ factory class instead. See documents below <br><br>
- The global setting `SEGMENT_ENDING_BAR` is deprecated and will be removed in a future release. If you're using this in your code, replace it with `WITH_SEGMENT_ENDING_FIELD_SEPARATOR`.
- Minimum supported PHP version has been updated to 8.0 <br>
Last supported versions: <br>
- PHP 7.0 or 7.1 => [1.5.4](https://github.com/senaranya/HL7/tree/1.5.4)<br>
- PHP 7.2 => [2.0.2](https://github.com/senaranya/HL7/tree/2.0.2)<br>
- PHP 7.4 => [2.1.7](https://github.com/senaranya/HL7/tree/2.1.7)
- The global setting `SEGMENT_ENDING_BAR` is deprecated and will be removed in a future release. Use
`WITH_SEGMENT_ENDING_FIELD_SEPARATOR` instead.
- PHP 8.1 => [3.1.7](https://github.com/senaranya/HL7/tree/3.1.8)

## Introduction

A PHP-based HL7 v2.x Parsing, Generation and Sending library, inspired from the famous Perl Net-HL7 package.
This is a PHP package to create, parse and send HL7 v2 messages. This was originally inspired from Net-HL7 Perl package.

## Installation

Expand All @@ -25,23 +26,13 @@ composer require aranyasen/hl7
```

## Usage
### Import library
```php
// 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
```php
// 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
use Aranyasen\HL7;
$message = HL7::from("MSH|^~\\&|1|")->create();
$message = HL7::from("MSH|^~\\&|1|\rPID|||abcd|\r")->create(); // Creates Message object with two segments with \r as segment ending (\n can also be used)

// Get string form of the message
echo $message->toString(true);
Expand All @@ -55,82 +46,87 @@ $message->getFirstSegmentInstance('ABC'); // Returns the first ABC segment. Same
$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
$message = HL7::build()->create();
$message->removeSegmentsByName('MSH')
$message->isEmpty(); // Returns true
```

### Composing new messages
```php
// 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()->create(); // Creates a Message containing MSH segment with default separators, version etc.
```
#### Configuring HL7 messages
```php
// The HL7 factory class provides methods that can be chained together in a fluent fashion. These can be used to
// override the defaults
$message = HL7::build()
->withComponentSeparator('#')
->withFieldSeparator('-')
->createMessage();

// Or, using Message class...
$message = new Message();
->withComponentSeparator('#') // Use # as the component separator instead of the default ^
->withFieldSeparator('-') // Use - as the field separator instead of the default |
->withSegmentSeparator('\r\n') // Override segment separator
->withHL7Version('2.3') // Use HL7 version 2.3
->create();
```
#### Message constructor parameters
```php
// 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);
// So to reset segment indices to 1:
HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->resetIndices()->create(); // Use resetIndices() while creating a new message
$message->resetSegmentIndices(); // or call resetSegmentIndices() on an existing $message object
// ... any segments added here will now start index from 1, as expected.
```
```php
// 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
$message = HL7::from($hl7String)
->autoIncrementIndices(false)
->create();
// $message now contains both OBXs with 1 as the index, instead of 1 and 2
```
```php
// 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
// Ensure empty sub-fields are not removed
// Note: We should perhaps _always_ use this while creating a message object, as we don't want empty subfields removed. In future versions, this will be the default
$message = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->create();
$pv1 = $message->getSegmentByIndex(1);
$fields = $pv1->getField(3); // $fields is ['', 'AAAA1', '', '', 'BB']

// Create/send message with segment-ending field-separator character (default "|") removed
use Aranyasen\HL7\Message;
$message = new Message("MSH|^~\\&|1|\nABC|||xxx\n", ['WITH_SEGMENT_ENDING_FIELD_SEPARATOR' => false]);
$message->toString(true); // Returns "MSH|^~\&|1\nABC|||xxx\n"
(new Connection($ip, $port))->send($message); // Sends the message without ending field-separator character (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']);

```
```php
// 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]]
$message = HL7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")->create(); // 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']
// To create a single array instead, use doNotSplitRepetition()
// Note: Since this leads to a non-standard behavior, it may be removed in future
$message = HL7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
->doNotSplitRepetition()
->create(); // Creates ['3', '0~4', '1']
```

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

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

// Create a custom segment
use Aranyasen\HL7\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"
$message->insertSegment($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
use Aranyasen\HL7\Segments\PID;
$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
Expand All @@ -148,7 +144,7 @@ Side note: In order to run Connection you need to install PHP ext-sockets [https
```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
$message = HL7::from($hl7String)->create(); // Create a Message object from your HL7 string. See above for details

// 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);
Expand All @@ -158,9 +154,10 @@ echo $response->toString(true); // Prints ACK from the listener
### ACK
Handle ACK message returned from a remote HL7 listener...
```php
use Aranyasen\HL7\Connection;
$ack = (new Connection($ip, $port))->send($message); // Send a HL7 to remote listener
$returnString = $ack->toString(true);
if (strpos($returnString, 'MSH') === false) {
if (str_contains($returnString, 'MSH') === false) {
echo "Failed to send HL7 to 'IP' => $ip, 'Port' => $port";
}
$msa = $ack->getFirstSegmentInstance('MSA');
Expand All @@ -175,12 +172,13 @@ else {
```
Create an ACK response from a given HL7 message:
```php
$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
use Aranyasen\HL7\Messages\ACK;
$msg = HL7::from("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|")->keepEmptySubfields()->create();
$ackResponse = new ACK($msg);
```
Options can be passed while creating ACK object:
```php
$msg = new Message("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|", null, true);
$msg = HL7::from("MSH|^~\\&|1|\rABC|1||^AAAA1^^^BB|")->keepEmptySubfields()->create();
$ackResponse = new ACK($msg, null, ['SEGMENT_SEPARATOR' => '\r\n', 'HL7_VERSION' => '2.5']);
```

Expand Down
16 changes: 16 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## 4.0
### Breaking changes
- First segment in the Message object can only be MSH. E.g. this will now fail: `(new Message())->addSegment(new Segment('XXX'))`, so it has to be `(new Message("MSH|^~\\&|1|\r"))->addSegment(new Segment('XXX'))`
- In `insertSegment()` method, only MSH can be inserted now in the 0th index
- Replaced all `InvalidArgumentException` with `HL7Exception`, so update the catches accordingly
- Dropped support for PHP 8.1. Minimum version required is now 8.2. So if your project can not be upgraded to 8.2, you'll need to continue using 3.x version
- `withSegmentSeparator` in HL7 accepts CRLF (\r\n) as argument. Any other multi-character separator will continue to throw exception
### Non-breaking changes
- Using `new Message()` is deprecated, and might be removed in a future version. Use HL7 factory to create a new HL7 object instead. See readme on how to use it
- `setSegment` method is deprecated. Use `insertSegment` instead
- In `insertSegment`, added a replace parameter to replace a current segment in place
- Updated API docs
- Added `replaceSegment` method
- PhpUnit bumped to v11
- Added `create` method in HL7 factory that is an alias to `createMessage`
- Code has been significantly cleaned up, especially in Message class
9 changes: 4 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
"minimum-stability": "dev",
"prefer-stable" : true,
"require": {
"php": "^8.0",
"php": ">=8.2",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^9",
"dms/phpunit-arraysubset-asserts": "^0.2",
"phpunit/phpunit": "^11.5.3",
"ext-pcntl": "*",
"ext-sockets": "*",
"squizlabs/php_codesniffer": "^3.6"
"squizlabs/php_codesniffer": "^3.11.3"
},
"autoload": {
"psr-4": {
Expand All @@ -35,7 +34,7 @@
}
},
"scripts": {
"test": "vendor/bin/phpunit tests/ --coverage-text",
"test": "vendor/bin/phpunit tests/ --coverage-text --testdox --colors=always",
"lint": "vendor/bin/phpcs"
},
"extra": {
Expand Down
Loading
Loading