In PHP development, the pack() function is often used to package data into binary strings and is widely used in scenarios such as network communication, file storage, and encrypted data structures. However, as the project iterations, the data formats of different versions may change, resulting in unpacking failures or data parsing errors. To solve this problem, we can build a PackFormatManager tool class that supports version compatibility, uniformly manage the pack() format definitions of each version, and achieve backward compatibility between data packaging and unpacking.
Suppose we have a binary data structure, and each field has a fixed encoding format, such as integers, floating point numbers, strings, etc. Early in the project we may have used only two fields:
user_id (4 byte integer)
timestamp (4 byte integer)
As the system upgrades, we add fields such as:
version 2: Added user_type (1 byte)
version 3: is_active (1 byte boolean value)
Every time the version is upgraded, we expect that the data of the older version will still be correctly identified and processed without throwing exceptions or read errors.
We want to implement a PackFormatManager class, which has the following functions:
Supports multi-version packaging format definition;
The data version can be automatically identified and unpacked;
Provide a unified interface for packaging and unpacking operations;
Provides backward compatibility and scalability of data;
Extensible new fields do not affect the processing of old version data.
<?php
class PackFormatManager
{
private $formats = [];
public function __construct()
{
$this->registerDefaultFormats();
}
private function registerDefaultFormats()
{
$this->formats = [
1 => [
'format' => 'Luser_id/Ltimestamp',
'fields' => ['user_id', 'timestamp'],
],
2 => [
'format' => 'Luser_id/Ltimestamp/Cuser_type',
'fields' => ['user_id', 'timestamp', 'user_type'],
],
3 => [
'format' => 'Luser_id/Ltimestamp/Cuser_type/Cis_active',
'fields' => ['user_id', 'timestamp', 'user_type', 'is_active'],
],
];
}
public function pack(array $data, int $version): string
{
if (!isset($this->formats[$version])) {
throw new InvalidArgumentException("Unsupported format version: $version");
}
$format = $this->formats[$version]['format'];
$values = [];
foreach ($this->formats[$version]['fields'] as $field) {
$values[] = $data[$field] ?? 0;
}
// Add version number header (1 byte)
return pack('C', $version) . pack($format, ...$values);
}
public function unpack(string $binary): array
{
// First parse the version number
$version = unpack('Cversion', $binary)['version'];
if (!isset($this->formats[$version])) {
throw new InvalidArgumentException("Unsupported format version: $version");
}
$format = $this->formats[$version]['format'];
$fields = $this->formats[$version]['fields'];
// Remove the version header and unpack it
$data = unpack($format, substr($binary, 1));
return array_merge(['version' => $version], $data);
}
public function getSupportedVersions(): array
{
return array_keys($this->formats);
}
}
$manager = new PackFormatManager();
$data = [
'user_id' => 1001,
'timestamp' => time(),
'user_type' => 2,
'is_active' => 1,
];
$packed = $manager->pack($data, 3);
echo "Packed binary: " . bin2hex($packed) . PHP_EOL;
$unpacked = $manager->unpack($packed);
print_r($unpacked);
When you want to add a new field, just add a format definition to the new version. Older versions can still be correctly identified and processed. For example:
$this->formats[4] = [
'format' => 'Luser_id/Ltimestamp/Cuser_type/Cis_active/Llogin_count',
'fields' => ['user_id', 'timestamp', 'user_type', 'is_active', 'login_count'],
];
To maintain good compatibility, you can also host the field description document at an address such as <code> https://m66.net/docs/pack-format-v4.json </code>, allowing the client to dynamically load and verify the field format.
By encapsulating the package() format management logic, we implement a flexible, extensible, backward compatible packaging management tool class. It not only improves code maintainability, but also provides a solid foundation for system upgrades and data persistence. This model is particularly important in the design of multi-terminal interaction, network communication and file data flow, and is recommended to be widely used in medium and large PHP projects.