PHPでは、 Pack()関数は、データをバイナリ文字列に変換するための非常に便利なツールであり、ネットワーク伝送またはファイル操作での使用に便利です。ただし、多くの開発者は、異なるプラットフォーム(Windows、Linux、MacOなど)で同じPack()機能を使用すると、パッケージ化されたバイナリデータがまったく同じではない場合があることがわかりました。なぜこれがなぜですか?この記事では、基礎となるメカニズム、プラットフォームの違い、ソリューションの観点から詳細に分析されます。
Pack()関数は、指定された形式に従って1つ以上のデータをバイナリ文字列にパッケージ化します。一般的な形式は次のとおりです。
C :署名された文字(1バイト)
C :署名されていない文字(1バイト)
S :署名された短い整数(2バイト)
S :署名されていない短い整数(2バイト)
L :署名された長い整数(4バイト)
L :署名されていない長い整数(4バイト)
F :単一の精度の浮動小数点数(4バイト)
D :ダブル精度の浮動小数点数(8バイト)
例えば:
$data = pack("Nn", 0x12345678, 0x1234);
echo bin2hex($data);
このコードは、ネットワークエンディアンネス(ビッグエンディアン)に整数をパッケージ化し、結果はどのプラットフォームでも一貫しているはずです。
違いは、主に、次のようなデフォルトでプラットフォームバイトの順序とデータ型サイズに依存するPack()の形式に記号があるためです。
SおよびS :バイトの順序とサイズはプラットフォームに依存する「短い整数」に対応します(通常は2バイトですが、非常に少数のプラットフォームでは異なる場合があります)。
LとL :「長い整数」に対応し、そのサイズとエンドネスもプラットフォーム(通常4バイト)に依存しますが、一部のプラットフォームは8バイトである場合があります。
さらに、エンディアン秩序には2つの主流があります。
Big-Endian :高いバイトは低い住所に保存されます
リトルエンディアン:低いバイトが低いアドレスに保存されます
さまざまなプラットフォーム上のCPUは、異なるエンディアンを使用します。
プラットフォーム/アーキテクチャ | バイトオーダー |
---|---|
Windows(x86/x64) | リトルエンディアン |
Linux(x86/x64) | リトルエンディアン |
MacOS(Intel) | リトルエンディアン |
macos(腕) | リトルエンディアン |
いくつかの組み込みプラットフォーム | おそらくビッグエンディアン |
Pack()のsとlは、マシンのネイティブエンディアンネスに依存するため、同じコードの出力は異なるアーキテクチャまたはオペレーティングシステムで異なります。
<?php
// 短い整数を梱包します 0x1234
$data = pack("s", 0x1234);
echo bin2hex($data);
?>
中小プラットフォーム(ほとんどのX86 Windows/Linuxなど)では、出力は次の場合があります。
ビッグエンディアンプラットフォームの出力結果は、 1234です
これは、 Sがプラットフォームのエンディアンネスに依存するためです。
Pack()の出力データがプラットフォーム間で一貫していることを確認するには、推奨されます。
バイト順序を指定するためにフォーマッタを使用します
PHPはネットワークバイトの注文形式を提供します。
N :署名のない短い整数(16ビット)、ネットワークエンディアン(ビッグエンディアン)
N :署名されていない長い整数(32ビット)、ネットワークエンディアンネス(ビッグエンディアン)
ネイティブのエンディアンネスに依存するSとLの使用を避け、代わりにNとNを使用します。
バイトオーダー変換をカスタマイズします
ネイティブのEndian形式を使用する必要がある場合は、最初にPack()を使用してから、 uppack()やstrrev()などの関数を使用してエンディアンネスを手動で変換できます。
明確なデータサイズ
データサイズが不確かな場合、 SとLを使用するのではなく、代わりにSとL (unsigned)を使用し、それをネットワークのエンディアンネスと組み合わせることが最善です。
<?php
// ネットワークのエンディアンネスを使用して、クロスプラットフォームの一貫性を確保します
$short = 0x1234;
$long = 0x12345678;
// pack署名されていない短い整数と署名されていない長い整数,すべてのネットワークバイト順序(ビッグエンディアン)
$data = pack("nN", $short, $long);
// 16進文字列を印刷します
echo bin2hex($data);
?>
Windows、Linux、またはMacOSで実行されるかどうかにかかわらず、出力は次のとおりです。
123412345678
Pack()のいくつかのフォーマッタは、プラットフォームのエンディアン性とデータサイズに依存し、一貫性のないクロスプラットフォーム出力をもたらします。
一般的に使用される形式は、 S 、 S 、 L 、およびLが異なるシステムで異なって機能する場合があります。
ネットワークバイトの順序形式nおよびnの使用は、データのクロスプラットフォームの一貫性を確保できます。
プラットフォームバイトの順序とデータ型サイズを理解することは、クロスプラットフォームのバイナリデータエラーを避けるための鍵です。
これらの詳細を理解することで、バイナリデータの構造をより適切に制御でき、プログラムのさまざまな環境での互換性の問題を回避できます。