Verdict: ReferenceOrderId suitable for payment gateway rather than uuid-v7
- Length optimization (30 vs 36 chars)
- Product identification via prefix
- Still k-sortable like UUIDv7
- Sufficient entropy (96 bits)
Example usage:
<?php
declare(strict_types=1);
use GatePay\Core\Utils\ReferenceOrderId;
$prefix = "INVC"
$orderIdGen = new ReferenceOrderId($prefix);
// Generate random
// INVC-019d43d20eb8-6a5a7925dfb5
$referenceId = $orderIdGen->generate();XMLParserArray is a utility class that provides methods to parse XML strings into associative arrays using three different approaches: LibXML, PureXML, and SimpleXML. Each method offers a unique way to handle XML parsing, allowing developers to choose the one that best suits their needs.
| Method | Description | Requirements |
|---|---|---|
parse() |
Auto-selects the best available parser | None (fallback to PureXML) |
parseSimpleXML() |
Uses PHP's SimpleXML extension | ext-simplexml, ext-libxml |
parseLibXML() |
Uses PHP's XML extension | ext-xml, ext-libxml |
parsePureXML() |
Pure PHP implementation (no extensions) | None |
All three parsers produce consistent output following these rules:
Elements without attributes return their text content directly as a string.
<item>value</item>['item' => 'value']Self-closing tags with attributes return an array with @attributes key containing all attributes, and @value key as empty string.
<item id="1" name="Item 1" />['item' => ['@attributes' => ['id' => '1', 'name' => 'Item 1'], '@value' => '']]Tags with attributes AND text content return @attributes and @value keys.
<item id="1">content</item>['item' => ['@attributes' => ['id' => '1'], '@value' => 'content']]Tags with attributes AND child elements merge @attributes with child element keys.
<item attribute1="value1">
<id>1</id>
<name>Item 1</name>
</item>['item' => ['@attributes' => ['attribute1' => 'value1'], 'id' => '1', 'name' => 'Item 1']]Multiple elements with the same tag name become an indexed array.
<root><item>first</item><item>second</item></root>['root' => ['item' => ['first', 'second']]]| Scenario | @attributes |
@value |
Child Keys |
|---|---|---|---|
<tag /> (no attrs) |
❌ | Returns '' directly |
❌ |
<tag attr="x" /> |
✅ | '' (empty) |
❌ |
<tag>text</tag> |
❌ | Returns 'text' directly |
❌ |
<tag attr="x">text</tag> |
✅ | 'text' |
❌ |
<tag attr="x"><child>y</child></tag> |
✅ | ❌ | ✅ 'child' => 'y' |
<tag><child>y</child></tag> |
❌ | ❌ | ✅ 'child' => 'y' |
<?php
declare(strict_types=1);
use GatePay\Core\Utils\XMLParserArray;
require __DIR__ .'/vendor/autoload.php';
// Example 1: Self-closing tags with attributes
// - Each <item /> has attributes but no content
// - Result: @attributes contains all tag attributes, value is empty string
$xml = <<<XML
<root>
<item id="1" name="Item 1" price="10.00" />
<item id="2" name="Item 2" price="20.00" />
<item id="3" name="Item 3" price="30.00" />
</root>
XML;
// Example 2: Tags with attributes AND child elements
// - Each <item> has attributes AND nested child elements
// - Result: @attributes contains tag attributes, children become separate keys
$xmlNamed = <<<XML
<root>
<item attribute1="value1" attribute2="value2">
<id>1</id>
<name>Item 1</name>
<price>10.00</price>
</item>
<item attribute1="value1" attribute2="value2">
<id>2</id>
<name>Item 2</name>
<price>20.00</price>
</item>
<item attribute1="value1" attribute2="value2">
<id>3</id>
<name>Item 3</name>
<price>30.00</price>
</item>
</root>
XML;
// Expected output for Example 1 (self-closing with attributes):
// - @attributes: contains id, name, price from tag attributes
// - value: empty string (self-closing tag has no content)
$expected = [
'root' => [
'item' => [
['@attributes' => ['id' => '1', 'name' => 'Item 1', 'price' => '10.00'], '@value' => ''],
['@attributes' => ['id' => '2', 'name' => 'Item 2', 'price' => '20.00'], '@value' => ''],
['@attributes' => ['id' => '3', 'name' => 'Item 3', 'price' => '30.00'], '@value' => '']
]
]
];
// Expected output for Example 2 (attributes + child elements):
// - @attributes: contains attribute1, attribute2 from tag attributes
// - id, name, price: child elements become separate keys with their text content
$expectedNamed = [
'root' => [
'item' => [
['@attributes' => ['attribute1' => 'value1', 'attribute2' => 'value2'], 'id' => '1', 'name' => 'Item 1', 'price' => '10.00'],
['@attributes' => ['attribute1' => 'value1', 'attribute2' => 'value2'], 'id' => '2', 'name' => 'Item 2', 'price' => '20.00'],
['@attributes' => ['attribute1' => 'value1', 'attribute2' => 'value2'], 'id' => '3', 'name' => 'Item 3', 'price' => '30.00']
]
]
];
// All three parsers produce identical output
$data = [
'attributes' => [
'LibXML' => XMLParserArray::parseLibXML($xml),
'PureXML' => XMLParserArray::parsePureXML($xml),
'SimpleXML' => XMLParserArray::parseSimpleXML($xml),
],
'attributes_named' => [
'LibXML' => XMLParserArray::parseLibXML($xmlNamed),
'PureXML' => XMLParserArray::parsePureXML($xmlNamed),
'SimpleXML' => XMLParserArray::parseSimpleXML($xmlNamed),
]
];
/*
* Expected result - all parsers should match:
*
* - LibXML matches expected output
* - PureXML matches expected output
* - SimpleXML matches expected output
* - LibXML matches expected output
* - PureXML matches expected output
* - SimpleXML matches expected output
*/
foreach ($data as $key => $parsers) {
$expectedData = match ($key) {
'attributes' => $expected,
'attributes_named' => $expectedNamed,
default => throw new Exception("Unexpected key: $key"),
};
foreach ($parsers as $parserName => $result) {
$matches = $result === $expectedData;
echo "- $parserName " . ($matches ? "matches" : "does NOT match") . " expected output\n";
if (!$matches) {
echo "Result:\n" . print_r($result, true) . "\nExpected:\n" . print_r($expectedData, true) . "\n";
}
}
}