From 0a96e6ca438b269a7ba9f71e9632bdc375a0e885 Mon Sep 17 00:00:00 2001
From: Aranya Sen
Date: Sat, 11 Jan 2025 13:10:00 +0530
Subject: [PATCH] Use HL7 factory as the primary method of creating Message
object
---
README.md | 63 ++++++++++++++++++++++++-----------------------
UPGRADE.md | 12 +++++++++
src/HL7.php | 8 ++++++
tests/HL7Test.php | 42 +++++++++++++++++++------------
4 files changed, 78 insertions(+), 47 deletions(-)
create mode 100644 UPGRADE.md
diff --git a/README.md b/README.md
index a245f8a..fb7d151 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,12 @@
**Important: "new Message()" is deprecated and could be removed in a future release. Use HL7 factory class instead. See documents below
+
Final releases for old PHP versions:
-> PHP 7.0/7.1 => [1.5.4](https://github.com/senaranya/HL7/tree/1.5.4)
-> PHP 7.2 => [2.0.2](https://github.com/senaranya/HL7/tree/2.0.2)
-> PHP 7.4 => [2.1.7](https://github.com/senaranya/HL7/tree/2.1.7)**
+-> PHP 8.1 => [3.1.7](https://github.com/senaranya/HL7/tree/3.1.7)**
## Introduction
@@ -35,10 +37,8 @@ use Aranyasen\HL7\Segments\MSH; // If MSH is used
### 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
+$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);
@@ -52,43 +52,41 @@ $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 = HL7::create();
$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
+$message = HL7::build()->create(); // 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();
+ ->create();
```
-#### Message constructor parameters
+#### Configuring HL7 messages
```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 given indexes in the string
```
```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']
@@ -98,16 +96,19 @@ $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']);
+$message = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")
+ ->withSegmentSeparator('\r\n')
+ ->withHL7Version('2.3')
+ ->create();
// 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
@@ -124,7 +125,7 @@ $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
@@ -145,7 +146,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);
@@ -172,12 +173,12 @@ else {
```
Create an ACK response from a given HL7 message:
```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);
```
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']);
```
diff --git a/UPGRADE.md b/UPGRADE.md
new file mode 100644
index 0000000..9c701dc
--- /dev/null
+++ b/UPGRADE.md
@@ -0,0 +1,12 @@
+## 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
+### 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
+ - Added `replaceSegment` method
+ - Added `create` method in HL7 factory that is an alias to `createMessage`
diff --git a/src/HL7.php b/src/HL7.php
index 7f6a971..606a19d 100644
--- a/src/HL7.php
+++ b/src/HL7.php
@@ -76,6 +76,14 @@ public function createMessage(): Message
);
}
+ /**
+ * @throws HL7Exception
+ */
+ public function create(): Message
+ {
+ return $this->createMessage();
+ }
+
/**
* Create a new MSH segment, using the global HL7 variables as defaults.
* @throws Exception
diff --git a/tests/HL7Test.php b/tests/HL7Test.php
index f0082fd..45c692d 100644
--- a/tests/HL7Test.php
+++ b/tests/HL7Test.php
@@ -16,7 +16,17 @@ class HL7Test extends TestCase
*/
#[Test] public function hl7_message_can_be_created_using_factory_class(): void
{
- $msg = HL7::build()->createMessage();
+ $msg = HL7::build()->create();
+ self::assertIsObject($msg);
+ self::assertSame(HL7\Message::class, $msg::class);
+ }
+
+ /**
+ * @throws HL7Exception
+ */
+ #[Test] public function hl7_message_can_be_created_using_create_alias_method(): void
+ {
+ $msg = HL7::build()->create();
self::assertIsObject($msg);
self::assertSame(HL7\Message::class, $msg::class);
}
@@ -26,7 +36,7 @@ class HL7Test extends TestCase
*/
#[Test] public function factory_creates_a_msh_segment_with_defaults_when_no_message_string_provided(): void
{
- $msg = HL7::build()->createMessage();
+ $msg = HL7::build()->create();
self::assertStringContainsString('MSH|^~\&|||||', $msg->toString(true));
}
@@ -35,7 +45,7 @@ class HL7Test extends TestCase
*/
#[Test] public function a_message_can_be_built_from_a_provided_hl7_string(): void
{
- $msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")->createMessage();
+ $msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")->create();
self::assertSame("MSH|^~\\&|1|\nABC|||xxx|\n", $msg->toString(true));
}
@@ -47,7 +57,7 @@ class HL7Test extends TestCase
self::markTestSkipped('property cannot be overridden yet in Message() constructor');
$msg = HL7::from("MSH|^~\\&|1|\rABC|||xxx|\r")
->withEscapeCharacter('=')
- ->createMessage();
+ ->create();
self::assertSame("MSH|^~=&|1|\nABC|||xxx|\n", $msg->toString(true));
}
/**
@@ -63,13 +73,13 @@ class HL7Test extends TestCase
->withRepetitionSeparator('`')
->withEscapeCharacter('=')
->withHL7Version('555.666')
- ->createMessage();
+ ->create();
self::assertStringContainsString('MSH#*`=}#', $msg->toString(true));
}
#[Test] public function empty_subfields_can_be_retained_if_needed(): void
{
- $msg = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->createMessage();
+ $msg = HL7::from("MSH|^~\\&|1|\rPV1|1|O|^AAAA1^^^BB|")->keepEmptySubfields()->create();
$this->assertSame(['', 'AAAA1', '', '', 'BB'], $msg->getSegmentByIndex(1)->getField(3));
}
@@ -79,7 +89,7 @@ class HL7Test extends TestCase
#[Test] public function indices_of_each_segment_can_be_reset_when_message_is_composed(): void
{
// Create a message with a PID segment
- $msg1 = HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
+ $msg1 = HL7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg1->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n",
@@ -88,7 +98,7 @@ class HL7Test extends TestCase
);
// Create another message with a PID segment
- $msg2 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
+ $msg2 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg2->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|2|\n",
@@ -99,12 +109,12 @@ class HL7Test extends TestCase
// Create another message with a PID segment, this time with resetIndices()
$msg3 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")
->resetIndices()
- ->createMessage();
+ ->create();
$msg3->addSegment(new PID());
self::assertSame("MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n", $msg3->toString(true), 'PID index resets to 1');
// Create a message with a PID segment
- $msg4 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
+ $msg4 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg4->addSegment(new PID());
self::assertSame(
"MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|2|\n",
@@ -112,7 +122,7 @@ class HL7Test extends TestCase
'PID index gets incremented'
);
- $msg5 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->createMessage();
+ $msg5 = Hl7::from("MSH|^~\&|||||||ORM^O01||P|2.3.1|")->create();
$msg5->resetSegmentIndices();
$msg5->addSegment(new PID());
self::assertSame("MSH|^~\&|||||||ORM^O01||P|2.3.1|\nPID|1|\n", $msg5->toString(true), 'PID index resets to 1');
@@ -126,7 +136,7 @@ class HL7Test extends TestCase
$hl7String = "MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n";
$msg = Hl7::from($hl7String)
- ->createMessage();
+ ->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|2||22^BB|\n",
$msg->toString(true),
@@ -135,7 +145,7 @@ class HL7Test extends TestCase
$msg = Hl7::from($hl7String)
->autoIncrementIndices(false)
- ->createMessage();
+ ->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "OBX|1||11^AA|\n" . "OBX|1||22^BB|\n",
$msg->toString(true)
@@ -143,7 +153,7 @@ class HL7Test extends TestCase
$msg = Hl7::from("MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "PID|||3^0\n")
->autoIncrementIndices(false)
- ->createMessage();
+ ->create();
self::assertSame(
"MSH|^~\&|||||||ORU^R01|00001|P|2.3.1|\n" . "PID|||3^0|\n",
$msg->toString(true),
@@ -157,7 +167,7 @@ class HL7Test extends TestCase
#[Test] public function repetition_separation_character_can_be_ignored(): void
{
$message = Hl7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
- ->createMessage();
+ ->create();
self::assertIsArray(
$message->getSegmentByIndex(1)->getField(3),
'By default repetition should be split into array'
@@ -165,7 +175,7 @@ class HL7Test extends TestCase
$message = Hl7::from("MSH|^~\&|||||||ADT^A01||P|2.3.1|\nPID|||3^0~4^1")
->doNotSplitRepetition() // Ignore repetition separator character
- ->createMessage();
+ ->create();
$patientIdentifierList = $message->getSegmentByIndex(1)->getField(3);
self::assertIsArray($patientIdentifierList);
self::assertSame(['3', '0~4', '1'], $patientIdentifierList);