W3docs

pack()

В этой статье рассматривается функция PHP pack(): обзор, принцип работы и примеры использования.

В этой статье рассматривается функция PHP pack(): что она делает, мини-язык форматных строк, влияние порядка байтов (endianness) на результат и практические примеры — включая обратное преобразование с помощью unpack().

Что делает pack()

pack() принимает обычные PHP-значения (целые числа, числа с плавающей точкой, строки) и записывает их побайтово в единую бинарную строку — строку, символы которой представляют собой сырые байты, а не читаемый текст.

pack(string $format, mixed ...$values): string
  • $format — компактная форматная строка, которая описывает по порядку способ кодирования каждого значения (тип, размер и порядок байтов).
  • ...$values — одно или несколько кодируемых значений, сопоставляемых слева направо с форматными кодами.

Возвращаемое значение — бинарная строка. Поскольку эти байты обычно не являются печатаемыми, в примерах ниже результат передаётся через bin2hex(), чтобы можно было увидеть точно, какие байты были получены (два шестнадцатеричных знака = один байт).

Когда это используется?

pack() используется всякий раз, когда PHP должен работать с форматом, определённым в сырых байтах, а не в виде текста:

  • Бинарные протоколы — формирование сетевых пакетов, где заголовок представляет собой «2-байтовую длину, за которой следует 4-байтовый идентификатор».
  • Бинарные форматы файлов — запись чанков PNG, заголовков WAV или любого формата с полями фиксированной ширины.
  • Вспомогательные хэш/крипто-операции — преобразование шестнадцатеричного дайджеста в сырую байтовую форму для передачи в hash_hmac() или openssl_*.
  • Взаимодействие с C/встроенными системами, ожидающими поля фиксированного размера и фиксированного порядка байтов.

Для обычного PHP-хранилища serialize() или json_encode() удобнее; pack() выигрывает тогда, когда важен именно байтовый макет.

Первый пример

php— editable, runs on the server

123 в шестнадцатеричной системе — 0x7b. Форматный код N означает «unsigned long (4 байта), сетевой порядок байтов», поэтому значение дополняется до четырёх байтов и записывается старшим байтом первым: 00 00 00 7b.

Чтение форматной строки

Форматная строка — это последовательность форматных кодов, каждый из которых при необходимости сопровождается счётчиком повторений:

N    one unsigned long
N4   four unsigned longs in a row
N*   as many unsigned longs as there are remaining values
A10  a 10-character space-padded string

Коды можно объединять для описания целой записи. Передаваемые значения должны соответствовать кодам по порядку:

<?php
// A 2-byte short (1) followed by a 4-byte long (16909060)
$header = pack('nN', 1, 16909060);

echo bin2hex($header); // 000101020304
?>

Здесь n даёт 00 01 (short 1), а N01 02 03 04 (long 16909060, что равно 0x01020304). Байты следуют строго в том порядке, в котором написаны коды.

Порядок байтов (endianness)

Одно и то же число может быть сохранено с байтами в двух противоположных порядках, и pack() предоставляет отдельный код для каждого:

  • Big-endian (он же сетевой порядок байтов) хранит наиболее значимый байт первым — коды n (short) и N (long).
  • Little-endian хранит наименее значимый байт первым — коды v (short) и V (long).
<?php
echo bin2hex(pack('N', 1)), "\n"; // 00000001  (big-endian)
echo bin2hex(pack('V', 1)), "\n"; // 01000000  (little-endian)
?>

Это важно, потому что получатель должен использовать тот же порядок для обратного чтения данных. Сетевые протоколы стандартизированы на big-endian (N/n); многие форматы файлов (и дампы памяти x86) используют little-endian (V/v). При сомнениях для обмена данными выбирайте N/n — это и есть гарантия «сетевого порядка байтов».

Распространённые форматные коды

Наиболее часто используемые коды (полный список см. в руководстве PHP):

КодЗначение
aСтрока с NUL-дополнением
AСтрока с дополнением пробелами
c / CЗнаковый / беззнаковый char (1 байт)
s / SЗнаковый / беззнаковый short, машинный порядок байтов (2 байта)
n / NБеззнаковый short / long, big-endian
v / VБеззнаковый short / long, little-endian
f / dFloat / double, машинный формат
H / hШестнадцатеричная строка, старший / младший полубайт первым

Строковые коды используют счётчик повторений как ширину поля, а не количество значений:

<?php
echo pack('A6', 'PHP'), "|"; // PHP   |  (padded to 6 chars with spaces)
?>

Полный цикл: pack() и unpack()

pack() записывает байты; unpack() читает их обратно в PHP-значения. Чтобы восстановить исходные данные, необходимо описать тот же макет, при этом unpack() дополнительно требует имя для каждого поля:

<?php
// Encode two fields
$binary = pack('Nn', 65536, 7);

// Decode using the same layout, naming each field
$values = unpack('Nfirst/nsecond', $binary);

echo $values['first'], ' ', $values['second']; // 65536 7
?>

Слэш (/) разделяет именованные поля в формате unpack(). Если макеты на обеих сторонах не совпадают, вы получите мусор — кодирование и декодирование тесно связаны.

Подводные камни

  • Коды и значения должны соответствовать. Передача меньшего числа значений, чем кодов, вызывает предупреждение и возвращает false; лишние значения молча игнорируются (если не использовался *).
  • Целочисленное переполнение усекается без предупреждения. pack('C', 300) сохраняет только младший байт (300 & 0xFF = 44) вместо того, чтобы выдать ошибку — валидируйте диапазоны самостоятельно.
  • Нативные коды (s, S, i, l, числа с плавающей точкой) не переносимы. Их размер и порядок байтов зависят от платформы. Для данных, передаваемых между разными машинами, предпочитайте явные коды big-/little-endian.
  • Результат — бинарная строка. Не выводите её на HTML-страницу и не сравнивайте как текст; изучайте с помощью bin2hex() или записывайте в бинарный файл/поток.

Для обратной операции перейдите к главе unpack(). Чтобы увидеть сырые байты в собственных экспериментах, незаменимым помощником, использованным на протяжении всей этой страницы, является bin2hex().

Заключение

pack() преобразует PHP-значения в точно управляемую бинарную строку с компактным форматным мини-языком для типа, размера и порядка байтов поля. Используйте её всякий раз, когда нужно создать байты, которые другая система читает по своим правилам — бинарные протоколы, форматы файлов или криптографические процедуры, — и сочетайте с unpack() для обратного чтения данных.

Практика

Практика
Что делает функция pack() в PHP?
Что делает функция pack() в PHP?
Was this page helpful?