Skip to content
Merged

4.x #104

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
851e267
Error checks code refactors generate docs (#95)
senaranya Jun 24, 2023
3a1a67d
Error checks code refactors generate docs (#100)
senaranya Sep 17, 2023
6cde4e0
Merge branch 'master' into 4.x
senaranya Sep 17, 2023
43ba750
Revert setSegment as per master
senaranya Sep 17, 2023
ba46482
Fix tests
senaranya Sep 17, 2023
4c1da02
Merge branch 'master' into 4.x
senaranya Jan 5, 2025
585cedf
Bump min PHP to 8.2. Misc post-rebase changes
senaranya Jan 6, 2025
b6f7d03
Improve test name
senaranya Jan 6, 2025
6fdc319
Upgrade PhpUnit to v11
senaranya Jan 6, 2025
985f02f
Better test output
senaranya Jan 6, 2025
0a96e6c
Use HL7 factory as the primary method of creating Message object
senaranya Jan 11, 2025
d3bfb59
withSegmentSeparator should accept CRLF (\r\n)
senaranya Jan 11, 2025
53e8a79
Merge branch 'master' into 4.x
senaranya Jan 25, 2025
b3111a1
update dependencies
senaranya Jan 25, 2025
3bacbe2
update docs
senaranya Jan 25, 2025
1f57ed0
update readme
senaranya Jan 25, 2025
1bce8b5
Merge branch 'master' into 4.x
senaranya Feb 10, 2025
fda03b7
Add rekey function in segments trait
senaranya Feb 10, 2025
84f55bc
Update version
senaranya Feb 10, 2025
562cb7c
Update readme; fix composer.json
senaranya Mar 16, 2026
9b7cafb
Merge branch 'master' into 4.x
senaranya Mar 16, 2026
ad40445
Update tests
senaranya Mar 16, 2026
d721872
Add missing field
senaranya Mar 16, 2026
18acfb5
Fix type and docblock
senaranya Mar 16, 2026
ba94798
Update docs
senaranya Mar 16, 2026
626b9c7
Update docs
senaranya Mar 16, 2026
e2a35da
Push action to avoid running actions twice
senaranya Mar 16, 2026
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
10 changes: 5 additions & 5 deletions .github/workflows/main_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ '8.0', '8.1', '8.2' ]
php-versions: [ '8.2', '8.3', '8.4', '8.5' ]
name: PHP ${{ matrix.php-versions }} Test on ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: pcntl, mbstring, sockets
- name: Check PHP Version
run: php -v
# - name: Validate composer.json and composer.lock
# run: composer validate --strict
- name: Validate composer.json
run: composer validate --strict
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: vendor
key: ${{ runner.os }}-php-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }}
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,
],
];
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* The goal of this library is to keep it generic and minimal. The focus should only be on basic HL7-related operations like creation/parsing/sending/receiving etc. No project-specific business logic should be included.
* This library follows PSR-12 standards for coding style. You may use [phpcs](https://github.com/squizlabs/PHP_CodeSniffer#getting-started) to verify your code.
* Add unit tests for code changes/updates. There are a number of existing tests, follow those as reference
* For a PR on bug, please include test(s) that reproduces the bug
* Contributors are requested to add or update phpdoc for any new or existing methods being worked on. After updating/adding, please update the API documentation as well (details below)

##### Generate/update API documentation (in Markdown format) from docblocks
Expand Down
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
135 changes: 78 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,42 @@
<a href="https://packagist.org/packages/aranyasen/hl7"><img src="https://poser.pugx.org/aranyasen/hl7/license" alt="License"></a>
</p>

## Release announcement:
### Version 4.0 (Breaking Release)
⚠️ **This release contains breaking changes.**
If you are upgrading from **3.x**, please read the [Guide](UPGRADE.md) before upgrading.

#### Key Changes
- Minimum supported PHP version is now **8.2**
- `Message` must start with a `MSH` segment
- `InvalidArgumentException` replaced with `HL7Exception`
- `new Message()` is now **deprecated** (use `HL7` factory instead)
- `setSegment()` is deprecated (use `insertSegment()` instead)
- In `insertSegment()` method, only MSH can be inserted now in the 0th index
- `withSegmentSeparator()` in HL7 factory accepts CRLF (`\r\n`) as argument. Any other multi-character separator will continue to throw exception

If something breaks after upgrading:
- Report bugs or request features at [Issues](https://github.com/senaranya/HL7/issues)
- Submit fixes at [Pull Requests](https://github.com/senaranya/HL7/pulls)

---

## Important
- 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.
- 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`

## PHP Compatibility

| PHP Version | Package Version |
|-------------|----------------|
| 8.2+ | 4.x |
| 8.0 – 8.1 | [3.2.2](https://github.com/senaranya/HL7/tree/3.2.2) |
| 7.4 | [2.1.7](https://github.com/senaranya/HL7/tree/2.1.7) |
| 7.2 | [2.0.2](https://github.com/senaranya/HL7/tree/2.0.2) |
| 7.0 – 7.1 | [1.5.4](https://github.com/senaranya/HL7/tree/1.5.4) |

## 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 +49,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 +69,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 +167,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 +177,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 +195,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
31 changes: 31 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## v4.0
### Upgrading to 4.0
1. Ensure your environment runs **PHP 8.2 or newer**
2. Update the dependency: `composer require aranyasen/hl7:^4.0`
3. Review the breaking changes and deprecations below and update your code accordingly
4. Run your test suite and verify message creation/parsing behavior

If you encounter problems, please [open an issue](https://github.com/senaranya/HL7/issues) or [create a pull request](https://github.com/senaranya/HL7/pulls).

### Breaking changes
- Minimum PHP version is now 8.2 (See readme for previous versions)
- First segment in `Message` objects must be MSH. E.g.
- ❌ Old behavior: `(new Message())->addSegment(new Segment('XXX'))`
- ✅ New behavior (4.0): `(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 your "catch"-blocks accordingly
- `withSegmentSeparator` in HL7 accepts CRLF (\r\n) as argument. Any other multi-character separator will continue to throw exception
### Deprecations
- ⚠️ Direct instantiation of `Message` is deprecated and may be removed in a future version.
Use the `HL7` factory instead, e.g. `$message = HL7::build()->create()`. See [README](README.md) on how to use it
- `setSegment()` is deprecated. Use `insertSegment()` instead

### New features
- In `insertSegment()`, added a replace parameter to replace a current segment in place
- Added `replaceSegment()` method
- Added `create()` method in HL7 factory that is an alias to `createMessage`

### Internal / project maintenance changes
- PHPUnit bumped to v11
- API docs updated
- Code has been significantly cleaned up, especially in Message class
Loading