在PHP開發中,有時我們需要將多個數據按指定格式打包成二進製字符串,方便網絡傳輸或文件存儲。 PHP提供的pack函數可以實現這個需求,但它需要傳入複雜的格式字符串,不同場景格式差異較大。本文將介紹如何用pack函數封裝一個支持多種格式的通用打包函數,提升代碼的複用性和靈活性。
pack函數的基本語法是:
string pack(string $format, mixed ...$values)
$format指定格式,如C表示無符號字符, n表示16位大端字節序整型等。
$values是對應格式的數據。
例如,將兩個無符號字符和一個16位大端整數打包:
$data = pack('CCn', 0x01, 0x02, 0x1234);
我們希望封裝的函數:
接受格式字符串和對應的值數組。
自動根據格式字符串將值逐個打包。
支持常見格式類型,且方便擴展。
處理參數和格式不匹配的錯誤。
示例函數簽名:
function universalPack(string $format, array $values): string
下面是一個基礎實現,支持C 、 n 、 N 、 a等格式:
function universalPack(string $format, array $values): string {
$result = '';
$formatLength = strlen($format);
$valueIndex = 0;
for ($i = 0; $i < $formatLength; $i++) {
$char = $format[$i];
$repeat = 1;
// 檢測是否有重複數量,如a4、C2等
if (isset($format[$i + 1]) && ctype_digit($format[$i + 1])) {
$repeat = (int)$format[$i + 1];
$i++;
}
for ($r = 0; $r < $repeat; $r++) {
if (!isset($values[$valueIndex])) {
throw new InvalidArgumentException("值數量與格式不匹配");
}
$value = $values[$valueIndex];
$valueIndex++;
switch ($char) {
case 'C': // 無符號字符
$result .= pack('C', $value);
break;
case 'n': // 16位大端字節序
$result .= pack('n', $value);
break;
case 'N': // 32位大端字節序
$result .= pack('N', $value);
break;
case 'a': // NUL填充字符串,長度由repeat決定
if ($repeat < 1) {
throw new InvalidArgumentException("a格式需要指定長度");
}
$str = str_pad(substr($value, 0, $repeat), $repeat, "\0");
$result .= $str;
// a格式一次處理所有repeat,valueIndex不遞增
$valueIndex--;
break;
default:
throw new InvalidArgumentException("不支持的格式字符: $char");
}
}
}
return $result;
}
假設要打包一個數據包,結構為:
1字節命令號( C )
2字節數據長度( n )
4字節數據ID( N )
10字節數據內容(字符串, a10 )
調用示例:
$command = 0x01;
$length = 14;
$dataId = 123456;
$content = "hello";
$packed = universalPack('CnNa10', [$command, $length, $dataId, $content]);
echo bin2hex($packed);
輸出為:
01000e0001e24068656c6c6f0000000000
pack函數很強大,但直接使用時格式與值容易出錯。
封裝一個通用的universalPack函數,簡化多格式數據的打包工作。
函數可以根據實際需求繼續增強,比如支持更多格式,支持小端序等。
實際項目中,也可以結合unpack函數實現對等的解包功能。