Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 81 · Практика

Hash: отпечатки файлов, HMAC и производные ключи

Полный справочник hash: от проверки выгрузки до подписи webhook, потокового HashContext и точного выбора двоичных форматов.

Заметить изменение файла или текста

Наглядная модель

Одна изменённая цифра — проверяем другие байты

Сравниваем доверенный исходник с полученной строкой. Обе стороны используют один алгоритм и одно представление.

  • Исходник: order=42

    Доверенные байты сохраняются как основание проверки.

    • hash('sha256', $original)

      Вычисление получает имя алгоритма и ровно эти байты.

    • $saved: 64 hex-знака

      Это 32 байта результата, записанные двумя знаками на каждый байт.

  • Получено: order=43

    Последняя цифра другая; значение переменной $original не меняется.

    • hash('sha256', $received)

      Тот же алгоритм обрабатывает полученные байты.

    • $saved === результат → false

      В этом примере отпечатки различаются: программа выводит changed.

Доверие к исходной сумме обязательно. Обычный hash не содержит секретного ключа и не доказывает подлинность отправителя; для этой задачи далее разбирается HMAC.

Вы отправили поставщику выгрузку и хотите проверить, что у него те же байты. Хеш — короткий результат вычисления по содержимому. При одинаковом алгоритме и одинаковых байтах результат повторяется. Сначала проверим это на строке: файл тоже представляет собой последовательность байтов.

Проверка выгрузки
  1. Сохранили доверенный отпечаток исходного текста
  2. hash получает имя sha256 и проверяемые байты
  3. Алгоритм возвращает строку из 64 hex-знаков
  4. Сравниваем строки: изменённый текст даёт другой отпечаток

$original — переменная с исходной строкой; = сохраняет значение. hash — имя функции, а два аргумента в скобках задают алгоритм и данные. Кавычки обозначают текст. === проверяет точное совпадение значений и типов. Тернарная запись условие ? одно : другое выбирает сообщение; echo печатает его. Здесь результат сравнения открытых контрольных сумм не является секретом.

Для первого чтения: переменные, аргументы и возвращаемое значение. ООП и собственный класс пока не нужны: одна готовая функция решает задачу.

Минимальный отпечаток известного текстаСамостоятельный пример
<?php
declare(strict_types=1);

$text = 'order=42';
$checksum = hash('sha256', $text);
echo 'order=42: ', $checksum, "\n";
Ожидаемый результат
order=42: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Одинаковые байты и алгоритм дают эту же строку. В карточках ниже сравниваем изменённую запись, приложенный файл и сообщение с HMAC. Контрольная сумма без доверенного источника не подтверждает отправителя.

Хеширует байты, а возвращает hex или двоичную строку

SHA-256 возвращает 256 бит, то есть 32 байта. По умолчанию PHP записывает каждый байт двумя шестнадцатеричными знаками: всего 64. binary=true оставляет 32 исходных байта, среди которых могут быть NUL и непечатаемые значения. Это всё ещё PHP string. Для показа используют bin2hex; для протокола выбирают ровно требуемое им представление, например Base64 от двоичного результата.

Один результат в двух представленияхСамостоятельный пример
<?php
declare(strict_types=1);

$hex = hash('sha256', 'abc');
$raw = hash('sha256', 'abc', true);
echo strlen($hex), '/', strlen($raw), "\n";
var_dump($hex === bin2hex($raw));
Ожидаемый результат
64/32
bool(true)

true — третий аргумент, он меняет только представление. Это не дополнительный уровень защиты. Передавать бинарную строку в JSON без согласованного кодирования нельзя.

Пробел JSON меняет подпись, хотя данные те жеСамостоятельный пример
<?php
declare(strict_types=1);

$a = '{"id":42}';
$b = '{"id": 42}';
var_dump(json_decode($a, true) === json_decode($b, true));
var_dump(hash('sha256', $a) === hash('sha256', $b));
Ожидаемый результат
bool(true)
bool(false)

Хеш не понимает смысл JSON. Аналогично важны LF/CRLF, BOM, кодировка, порядок полей и Unicode-нормализация. Если протокол подписывает raw body, его проверяют до decode/re-encode; если требует канонизацию, обе стороны реализуют именно её спецификацию.

Контрольная сумма, HMAC, пароль и шифрование

ПотребностьПодходГраница
Выявить случайное повреждениеCRC/adler или согласованный SHA-256CRC легко подделать; доверие не возникает из самой суммы.
Индекс/распределение в таблицеНекриптографические xxHash/MurmurКоллизии возможны; храните исходный ключ для проверки.
Сверить содержимое по доверенному манифестуhash_file с SHA-256MD5/SHA-1 не выбирают для защиты от намеренной подмены.
Проверить сообщение от обладателя общего секретаhash_hmac и hash_equalsНе шифрует и не защищает от повторной доставки сам по себе.
Сохранить пароль пользователяpassword_hash / password_verifyБыстрый hash, даже SHA-256 с солью, не заменяет парольный алгоритм.
Получить отдельные ключи для разных задачhash_hkdfТребует качественный исходный ключ; не делает слабый пароль сильным.
Воспроизвести PBKDF2-контракт внешнего форматаhash_pbkdf2Параметры и дорогое вычисление согласуют отдельно; для обычного входа проще password_hash.
Скрыть содержимое и потом восстановить егоАутентифицированное шифрование из Sodium/OpenSSLHash не является обратимым шифрованием; HMAC не цифровая подпись с открытым ключом.

Хеши фиксированной длины неизбежно имеют коллизии. Криптографический алгоритм должен затруднять практически полезный поиск подмены, а не обещать математическую уникальность любых данных. Публичный хеш короткого значения, например номера телефона, допускает перебор кандидатов и не является анонимизацией. HMAC тоже не даёт неотказуемость: общий секрет известен обеим сторонам.

Что установлено и какие настройки существуют

С PHP 7.4 расширение hash входит в обязательную часть PHP. Раньше его можно было отключить при сборке через --disable-hash; до 5.1.2 оно распространялось через PECL. Специальных INI-настроек у этой книги нет. Алгоритм, режим и ключ передаются в конкретный вызов; проверить окружение помогают php --ri hash и списки алгоритмов.

Проверить среду и фабрику контекстаСамостоятельный пример
<?php
declare(strict_types=1);

echo extension_loaded('hash') ? 'hash: yes' : 'hash: no';
echo "\n";
$context = hash_init('sha256');
echo get_debug_type($context), "\n";
var_dump(is_resource($context));
Ожидаемый результат
hash: yes
HashContext
bool(false)

В PHP 7.2+ фабрика возвращает объект HashContext. Старая официальная страница Resource Types описывает предыдущий resource-контракт; переносить эту проверку в современный код нельзя.

HASH_HMAC — целочисленный флаг для hash_init. Передать ключ третьим аргументом при flags=0 недостаточно: HMAC включается именно флагом. Допустимый прикладной выбор — 0 либо HASH_HMAC; произвольные биты не являются документированными режимами. Сам ключ хранится в конфигурации/хранилище секретов, а не приходит из тела подписываемого запроса.

Алгоритмы и их options: выбираем явно

СемействоИмена из современного списка
SHAsha1; sha224, sha256, sha384, sha512/224, sha512/256, sha512; sha3-224, sha3-256, sha3-384, sha3-512
Исторические/специализированныеmd2, md4, md5; ripemd128/160/256/320; whirlpool; tiger128,3 / tiger160,3 / tiger192,3 и варианты ,4; snefru, snefru256; gost, gost-crypto; haval128/160/192/224/256 с ,3 / ,4 / ,5
Некриптографическиеadler32; crc32, crc32b, crc32c; fnv132, fnv1a32, fnv164, fnv1a64; joaat; murmur3a, murmur3c, murmur3f; xxh32, xxh64, xxh3, xxh128

Наличие имени в списке — совместимость реализации, а не рекомендация безопасности. SHA-3 и усечённые SHA-512 появились в 7.1; crc32c — в 7.4; MurmurHash3 и xxHash — в 8.1. Не заменяйте алгоритм молча на «первый доступный»: другая сторона получит другой результат.

options в PHP 8.5.11Правило
MurmurHash3seed — int; при отсутствии 0. Для переносимых учебных вызовов берём небольшое неотрицательное целое.
xxh32 / xxh64Также поддерживают int seed. seed меняет распределение, но не превращает алгоритм в HMAC.
xxh3 / xxh128int seed либо string secret; одновременно нельзя. secret минимум 136 байтов, максимум используемой части 256; более длинный вызывает warning и обрезку. Имя secret не делает xxHash криптографическим.
SHA-256 и другие алгоритмы без таких настроекПередавайте []. Неизвестные options могут игнорироваться, поэтому массив не является универсальным валидатором параметров.

Общие страницы пока перечисляют только Murmur seed. Дополнение про xxHash сверено с исходниками PHP 8.5.11. В PHP 8.4+ неверные типы seed/secret deprecated; их нельзя использовать как способ преобразования строковых настроек. Для xxh3/xxh128 короткий secret и одновременные seed+secret дают Error. Значения параметров, влияющих на результат, сохраняют вместе с выбранным алгоритмом.

Явный seed воспроизводит индексный отпечатокСамостоятельный пример
<?php
declare(strict_types=1);

$options = ['seed' => 7];
$direct = hash('murmur3a', 'product:42', false, $options);
$context = hash_init('murmur3a', 0, '', $options);
hash_update($context, 'product:42');
var_dump($direct === hash_final($context));
var_dump($direct !== hash('murmur3a', 'product:42', false, ['seed' => 8]));
Ожидаемый результат
bool(true)
bool(true)

Это детерминированное распределение ключа, не подпись заказа. Для HMAC требуется криптографический алгоритм и ключ в отдельном параметре.

Большой файл: накопить части, затем завершить

Жизнь контекста
  1. hash_init создаёт отдельное состояние алгоритма
  2. hash_update добавляет очередные байты в прежнее состояние
  3. hash_copy ответвляет независимую копию, если нужен промежуточный результат
  4. hash_final выдаёт отпечаток и закрывает именно этот контекст

HashContext хранит состояние вычисления и небольшой рабочий буфер, а не коллекцию всех переданных строк. При этом hash($algorithm, file_get_contents(...)) сначала создаёт большую строку вне алгоритма. Для одного файла подходит hash_file; для уже открытого потока — hash_update_stream. Все байты всё равно должны быть прочитаны: время зависит от объёма, диска/потока, алгоритма и оборудования. Нельзя обещать постоянное время чтения большого файла или переносить одно измерение на другой сервер.

Проверка содержимого и последующее чтение по тому же пути — две операции: файл между ними может измениться. Для недоверенного загрузочного процесса сначала фиксируют файл в контролируемом хранилище и применяют свою политику доступа. Пользовательский URL как filename может обратиться во внешнюю сеть или локальные wrapper-ресурсы; примеры ниже используют только собственные локальные fixtures.

HashContext: объект состояния, а не набор готовых хешей

Для обычной строки достаточно hash; объект нужен, когда текст поступает частями. HashContext — внутренний final-класс PHP. Его создаёт hash_init, а операции update/final принимают именно этот объект. Отдельный PHP-объект не переживает новый HTTP-запрос сам по себе.

Владение состоянием
  1. hash_init создаёт контекст A
  2. $other = $context указывает на тот же A
  3. hash_copy создаёт независимый B с тем же префиксом
  4. final(A) закрывает A; B можно продолжить

Не путайте внутренний HashContext со stream context: последний настраивает чтение/запись, первый накапливает криптографическое вычисление. Простой assignment не копирует объект; поэтому изменение через одну переменную видно через другую. Классы и стрелка метода разобраны в основах объектов.

МеханизмКогда нужен
КонструкторЗакрыт; используем фабрику hash_init.
Копияhash_copy для ответвления или промежуточного digest.
Диагностика 8.4+__debugInfo показывает algo, не раскрывает полный рабочий буфер.
Сериализация 8.0+Только доверенные поддерживаемые снимки; HMAC запрещён, переносимость не гарантирована.

Справочник и упражнение на выбор механизма

Функция / методЗадача
hashПолучить воспроизводимый отпечаток уже имеющейся строки.
hash_algosУзнать алгоритмы, реально зарегистрированные в текущем PHP.
hash_copyОтветвить независимое вычисление после общего префикса.
hash_equalsСравнить проверяемую секретную строку без раннего выхода по первому отличающемуся байту.
hash_fileПрочитать локальный файл потоково и вычислить отпечаток содержимого.
hash_finalПолучить окончательный отпечаток и завершить конкретный контекст.
hash_hkdfПолучить отдельный ключ для конкретной роли из исходного ключевого материала.
hash_hmacВычислить код аутентификации сообщения с общим секретом.
hash_hmac_algosПолучить список алгоритмов, для которых PHP реализует HMAC.
hash_hmac_fileВычислить MAC содержимого файла с известным обеим сторонам ключом.
hash_initСоздать состояние вычисления, в которое данные будут поступать частями.
hash_pbkdf2Воспроизвести согласованное PBKDF2-выведение ключа из пароля.
hash_updateДобавить очередную строку байтов к активному вычислению.
hash_update_fileДобавить весь файл к уже накопленному префиксу вычисления.
hash_update_streamДобавить байты из уже открытого потока, начиная с его текущей позиции.
HashContext::__constructПонять, почему контекст создают hash_init, а не через new.
HashContext::__debugInfoУвидеть алгоритм контекста в диагностическом выводе.
HashContext::__serializeСохранить поддерживаемое активное состояние обычного хеширования.
HashContext::__unserializeВосстановить собственный доверенный снимок HashContext для продолжения вычисления.

Упражнение. Для архива экспорта есть доверенный SHA-256 манифест; от платёжного сервиса приходит JSON с HMAC-SHA-256 в Base64; пользователь вводит пароль в форму. Выберите три разные проверки. Что поменяется, если JSON пересохранить с отступами? Что произойдёт, если повторить уже правильно подписанное событие?

Разбор. Архив читаем через hash_file и сравниваем с доверенным манифестом. Webhook проверяем по точным согласованным байтам через hash_hmac с binary=true, Base64 и hash_equals; дополнительно по протоколу проверяем подписанную свежесть и атомарно учитываем event-id в постоянном хранилище. Пароль проверяем password_verify по результату password_hash. Переформатирование JSON меняет байты и подпись; повторная доставка сохраняет корректную подпись, поэтому сама криптография её не отвергает. Примеры hash_hmac и hash_equals ниже позволяют воспроизвести первый сбой локально.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

hashПолучить воспроизводимый отпечаток уже имеющейся строки.

PHP 5.1.2+ · hash

hash(string $algo, string $data, bool $binary = false, array $options = []): string

Заметить изменение номера заказа. В hash('sha256', $text) первый аргумент выбирает алгоритм, второй задаёт точные байты. Возвращается строка отпечатка; исходный текст не меняется. === сравнивает и значение, и тип.

Путь конкретных данных
  1. order=42 → sha256 → доверенный отпечаток
  2. Получено order=43 → новый отпечаток
  3. Сравнение замечает изменение и останавливает импорт

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Получить воспроизводимый отпечаток уже имеющейся строки.

Параметр / результатКонтракт
algoИмя из hash_algos, выбранное по контракту задачи; например sha256.
dataТочные байты сообщения, включая пробелы, NUL и переводы строк. Пустая строка допустима.
binaryfalse — нижний hex; true — исходные двоичные байты.
optionsНастройки выбранного алгоритма; [] для SHA-256. seed/secret разобраны в разделе options.
returnНовая строка с отпечатком; исходная data не меняется. Её длина зависит от алгоритма и binary.
Заметить изменение номера заказаСамостоятельный пример
<?php
declare(strict_types=1);

$original = 'order=42';
$trusted = hash('sha256', $original);
echo 'Отпечаток order=42: ', $trusted, "\n";
$received = 'order=43';
echo $trusted === hash('sha256', $received) ? "Заказ не изменён.\n" : "Заказ изменён; импорт остановлен.\n";
Ожидаемый результат
Отпечаток order=42: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705
Заказ изменён; импорт остановлен.

Доверенный отпечаток должен храниться отдельно от недоверенной доставки. Если посторонний может заменить и файл, и его сумму, SHA-256 сам не подтвердит отправителя; для общего секрета нужен HMAC, для паролей — password_hash.

Версия преобразования включена в ключ, иначе изменение настроек оставит прежний кэш. Фиксированный namespace с разделителем и фиксированным hex-хвостом однозначен. Для нескольких произвольных полей нельзя просто склеить строки: пары ab+c и a+bc совпадут; сначала задают однозначное кодирование полей.

Версионировать результат обработки картинкиСамостоятельный пример
<?php
declare(strict_types=1);

$content = 'demo image bytes';
$pipeline = 'thumbnail-v2';
$key = $pipeline . ':' . hash('sha256', $content);
$same = $pipeline . ':' . hash('sha256', $content);
$next = 'thumbnail-v3:' . hash('sha256', $content);
echo str_starts_with($key, 'thumbnail-v2:') ? 'versioned' : 'bad';
echo "\n";
var_dump($key === $same, $key !== $next);
Ожидаемый результат
versioned
bool(true)
bool(true)

catch здесь показывает контракт ошибки. В приложении исправляют доверенную конфигурацию, а не подставляют пустой хеш и не принимают непроверенный алгоритм от клиента.

Отличить опечатку алгоритма от пустого результатаСамостоятельный пример
<?php
declare(strict_types=1);

try {
    hash('sha-unknown', 'abc');
} catch (ValueError $error) {
    echo 'invalid algorithm';
}
Ожидаемый результат
invalid algorithm

Ошибки и ограничения. Неизвестный algo вызывает ValueError с PHP 8; раньше были warning и false. options добавлен в 8.1, неверные типы настроек некоторых алгоритмов deprecated с 8.4. См. точные правила options. Быстрый digest не заменяет парольный алгоритм и не скрывает содержание.

Продолжить: hash_file, hash_init, hash_hmac. Как выбрать задачу и алгоритм.

hash_algosУзнать алгоритмы, реально зарегистрированные в текущем PHP.

PHP 5.1.2+ · hash

hash_algos(): array

Проверить алгоритм перед проверкой экспорта. Список hash_algos отражает возможности текущего процесса, включая алгоритмы, которые не предназначены для защиты. Наличие имени не означает, что его разрешила политика сервиса. Выбираем sha256 явно и отказываем при отсутствии.

Путь конкретных данных
  1. Проект требует sha256
  2. hash_algos перечисляет доступные имена
  3. Проверенное имя применяется к order=42

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Узнать алгоритмы, реально зарегистрированные в текущем PHP.

Параметр / результатКонтракт
ПараметрыНет: результат описывает расширение в этом процессе, а не удалённый сервер.
returnИндексированный массив строк — имён алгоритмов. Наличие имени не является оценкой его безопасности.
Проверить алгоритм перед проверкой экспортаСамостоятельный пример
<?php
declare(strict_types=1);

if (!in_array('sha256', hash_algos(), true)) { throw new RuntimeException('Нет обязательного sha256'); }
echo "Проверка экспорта: sha256 доступен.\n";
echo 'Контрольный результат: ', hash('sha256', 'order=42'), "\n";
Ожидаемый результат
Проверка экспорта: sha256 доступен.
Контрольный результат: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Фиксированный ключ здесь только для известного тестового вектора; настоящие ключи хранят закрыто и генерируют случайно. Алгоритм из запроса нельзя принимать только потому, что он есть в этом списке.

Расширение может поддерживать устаревший алгоритм для совместимости. Политика сервиса определяется отдельно; переключение алгоритма, если оно вообще нужно протоколу, должно входить в защищённые параметры.

Разделить доступность и разрешённый списокСамостоятельный пример
<?php
declare(strict_types=1);

$allowed = ['sha256', 'sha512'];
$available = hash_algos();
foreach (['sha256', 'md5'] as $requested) {
    $ok = in_array($requested, $allowed, true)
        && in_array($requested, $available, true);
    echo $requested, ':', $ok ? 'allowed' : 'rejected', "\n";
}
Ожидаемый результат
sha256:allowed
md5:rejected

Ошибки и ограничения. Нет параметров конфигурации и специального значения false. Ветки SHA-3/sha512 truncation добавлены в 7.1, crc32c в 7.4, Murmur/xxHash в 8.1. Список алгоритмов текущей версии перечислен в таблице семейств.

Продолжить: hash_hmac_algos, hash. Как выбрать задачу и алгоритм.

hash_fileПрочитать локальный файл потоково и вычислить отпечаток содержимого.

PHP 5.1.2+ · hash

hash_file(string $algo, string $filename, bool $binary = false, array $options = []): string|false

Проверить содержимое полученной выгрузки. Файл export.txt приложен: две строки order=42 и qty=2, каждая заканчивается LF. hash_file читает его содержимое, не имя. Путь __DIR__ задаёт каталог example.php.

Путь конкретных данных
  1. export.txt: order=42 LF qty=2 LF
  2. hash_file читает все15байтов файла
  3. SHA256 сравнивается с заранее заданным манифестом

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Прочитать локальный файл потоково и вычислить отпечаток содержимого.

Параметр / результатКонтракт
algoПоддерживаемый алгоритм, например sha256.
filenameПуть или поддерживаемый fopen wrapper. Хешируется содержимое, не имя файла и не его время изменения.
binaryfalse — нижний hex; true — двоичный результат.
optionsНастройки алгоритма, добавлены в PHP 8.1; не stream context.
returnDigest string либо false при невозможности открыть/прочитать файл. false проверяют строго.
Проверить содержимое полученной выгрузкиСамостоятельный пример
<?php
declare(strict_types=1);

$expected = 'c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987';
$actual = hash_file('sha256', __DIR__ . '/export.txt');
if ($actual === false) { throw new RuntimeException('Выгрузка не прочитана'); }
echo 'export.txt: ', $actual, "\n";
echo hash_equals($expected, $actual) ? "Заказ42, количество2: файл соответствует манифесту.\n" : "Импорт запрещён: файл изменён.\n";
Ожидаемый результат
export.txt: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987
Заказ42, количество2: файл соответствует манифесту.

Манифест в этом опыте задан доверенной константой в коде. Измените qty=2 на qty=9 и получите отказ. CRLF, BOM или дополнительный перевод строки тоже меняют байты и отпечаток.

Для дедупликации важен контент; имя, метаданные и права доступа нужно учитывать отдельно. Строгое сравнение здесь относится к открытым отпечаткам. Если это секретный MAC, выбирайте hash_equals.

Два имени файла, одинаковые байтыСамостоятельный пример
<?php
declare(strict_types=1);

$left = hash_file('sha256', __DIR__ . '/first.csv', true);
$right = hash_file('sha256', __DIR__ . '/second.csv', true);
if ($left === false || $right === false) { throw new RuntimeException('Read failed'); }

echo $left === $right ? "first.csv и second.csv содержат одинаковые байты.\n" : "Содержимое CSV различается.\n";
Ожидаемый результат
first.csv и second.csv содержат одинаковые байты.

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Только учебная проверка намеренно перехватывает ожидаемое warning. В рабочем процессе отказ чтения останавливает проверку; предупреждения не скрывают глобально и пути/данные не выводят клиенту.

Не принять ошибку чтения за пустой файлСамостоятельный пример
<?php
declare(strict_types=1);

$warnings = 0;
set_error_handler(function (int $level) use (&$warnings): bool {
    if ($level !== E_WARNING) { return false; }
    ++$warnings;
    return true;
});
try { $result = hash_file('sha256', __DIR__ . '/absent.txt'); }
finally { restore_error_handler(); }
echo $result === false && $warnings > 0 ? "Импорт остановлен: файл не прочитан, предупреждение получено.\n" : "Отказ не обнаружен.\n";
Ожидаемый результат
Импорт остановлен: файл не прочитан, предупреждение получено.

Ошибки и ограничения. Неверный algo и NUL в пути дают ValueError; невозможность чтения — warning/false. Интерфейс работает с wrappers, поэтому filename из запроса создаёт отдельный риск доступа к файлам/сети. В PHP 8.5 реализация читает блоками; файловый кэш ОС при этом может расти. Потоковое чтение не гарантирует неизменность файла между проверкой и использованием.

Продолжить: hash, hash_update_file, hash_update_stream, hash_hmac_file. Как выбрать задачу и алгоритм.

hash_initСоздать состояние вычисления, в которое данные будут поступать частями.

PHP 5.1.2+ · hash

hash_init(string $algo, int $flags = 0, string $key = "", array $options = []): HashContext

Получить отпечаток отчёта из двух строк. Создаёт пустое состояние вычисления. Переменная context хранит объект, который связывает следующие update и final. init сам не получает пользовательские байты.

Путь конкретных данных
  1. hash_init(sha256) создаёт пустой контекст
  2. update добавляет order=42 LF, затем qty=2 LF
  3. final возвращает отпечаток целого отчёта

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Создать состояние вычисления, в которое данные будут поступать частями.

Параметр / результатКонтракт
algoИмя алгоритма. Без HASH_HMAC разрешены и некриптографические алгоритмы.
flags0 — обычный digest; HASH_HMAC — режим с секретным ключом.
keyНепустой общий секрет, если flags включает HASH_HMAC. При flags=0 не включает HMAC и не участвует как такой ключ.
options[] по умолчанию; настройки алгоритма, например целочисленный seed для Murmur. Не путать с flags.
returnНовый независимый HashContext. В нём ещё нет пользовательских данных: следующий шаг — update.
Получить отпечаток отчёта из двух строкСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, "order=42\n");
hash_update($context, "qty=2\n");
$digest = hash_final($context);
echo 'Весь отчёт: ', $digest, "\n";
echo "Строки добавлены в порядке: order=42, затем qty=2.\n";
Ожидаемый результат
Весь отчёт: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987
Строки добавлены в порядке: order=42, затем qty=2.

Конкатенация этих частей равна точному содержимому export.txt из соседней карточки. Это один хеш всего отчёта; отдельно хешировать строки и соединять их хеши означало бы другой алгоритм.

Ключ здесь — открытый fixture, использовать его в сервисе нельзя. Реальный секрет известен доверенным сторонам; при отсутствии конфигурации сервис должен отказать в проверке, а не работать с пустой строкой.

Включить HMAC явно, а не только передать ключСамостоятельный пример
<?php
declare(strict_types=1);

$key = 'demo-only-key';
$context = hash_init('sha256', HASH_HMAC, $key);
hash_update($context, 'order:');
hash_update($context, '42');
var_dump(hash_final($context) === hash_hmac('sha256', 'order:42', $key));
try { hash_init('sha256', HASH_HMAC, ''); }
catch (ValueError $error) { echo 'missing key rejected'; }
Ожидаемый результат
bool(true)
missing key rejected

Ошибки и ограничения. ValueError: неизвестный algo; некриптографический algo именно в HMAC-режиме; пустой ключ именно при HASH_HMAC. Обычный hash_init без ключа корректен. С 7.2 возвращается объект вместо resource; с 8.1 есть options; неверные типы некоторых options deprecated с 8.4. Отдельные правила xxHash смотрите в options.

Продолжить: hash_update, hash_final, hash_copy, hash_hmac. Как выбрать задачу и алгоритм.

hash_updateДобавить очередную строку байтов к активному вычислению.

PHP 5.1.2+ · hash

hash_update(HashContext $context, string $data): true

Получить отпечаток отчёта из двух строк. Добавляет следующую часть без разделителей. Поэтому LF пишем явно в строке. Результат update сообщает успех; окончательный digest возвращает отдельный final.

Путь конкретных данных
  1. Контекст sha256 уже создан
  2. Первый update: order=42 LF; второй: qty=2 LF
  3. final считает обе части в исходном порядке

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Добавить очередную строку байтов к активному вычислению.

Параметр / результатКонтракт
contextАктивный, ещё не финализированный HashContext из hash_init.
dataСледующая порция байтов; пустая строка допустима. Разделители автоматически не добавляются.
returntrue при нормальном завершении. С PHP 8.4 это также точный объявленный return type вместо bool.
Получить отпечаток отчёта из двух строкСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, "order=42\n");
hash_update($context, "qty=2\n");
$digest = hash_final($context);
echo 'Весь отчёт: ', $digest, "\n";
echo "Строки добавлены в порядке: order=42, затем qty=2.\n";
Ожидаемый результат
Весь отчёт: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987
Строки добавлены в порядке: order=42, затем qty=2.

Конкатенация этих частей равна точному содержимому export.txt из соседней карточки. Это один хеш всего отчёта; отдельно хешировать строки и соединять их хеши означало бы другой алгоритм.

foreach берёт строки по порядку; здесь формат запрещает переводы строк внутри значения. Для произвольных полей нужен однозначный формат, например длина каждого поля перед его байтами. Hash не знает границы записей и не исправит неоднозначное склеивание.

Добавить записи с явными разделителямиСамостоятельный пример
<?php
declare(strict_types=1);

$records = ['order=42', 'qty=2'];
$context = hash_init('sha256');
foreach ($records as $record) {
    hash_update($context, $record . "\n");
}
$digest = hash_final($context);
echo "Проверены строки order=42 и qty=2 с LF.\nОтпечаток: ", $digest, "\n";
Ожидаемый результат
Проверены строки order=42 и qty=2 с LF.
Отпечаток: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Ошибки и ограничения. Неверный тип или уже финализированный context вызывает TypeError в PHP 8.5; true относится только к успешному вызову. С 7.2 параметр — HashContext. Порядок частей важен; хеш хешей отдельных блоков обычно не равен хешу исходного файла.

Продолжить: hash_init, hash_final, hash_update_file, hash_update_stream. Как выбрать задачу и алгоритм.

hash_finalПолучить окончательный отпечаток и завершить конкретный контекст.

PHP 5.1.2+ · hash

hash_final(HashContext $context, bool $binary = false): string

Получить отпечаток отчёта из двух строк. Получает окончательную строку и завершает состояние. Сохраните результат в переменной: повторный final/update того же контекста недопустим.

Путь конкретных данных
  1. Контекст накопил15байтов отчёта
  2. final возвращает digest и закрывает вычисление
  3. Повторно используем строку результата; для новых данных нужен новый контекст

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Получить окончательный отпечаток и завершить конкретный контекст.

Параметр / результатКонтракт
contextАктивный HashContext. После вызова хранится тот же объект, но продолжать вычисление им уже нельзя.
binaryfalse — нижний hex, true — исходные байты результата.
returnСтрока digest/HMAC для всех ранее добавленных байтов в исходном порядке.
Получить отпечаток отчёта из двух строкСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, "order=42\n");
hash_update($context, "qty=2\n");
$digest = hash_final($context);
echo 'Весь отчёт: ', $digest, "\n";
echo "Строки добавлены в порядке: order=42, затем qty=2.\n";
Ожидаемый результат
Весь отчёт: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987
Строки добавлены в порядке: order=42, затем qty=2.

Конкатенация этих частей равна точному содержимому export.txt из соседней карточки. Это один хеш всего отчёта; отдельно хешировать строки и соединять их хеши означало бы другой алгоритм.

Сохраняйте возвращённую строку, если она нужна ещё раз. Для промежуточного просмотра делают hash_final(hash_copy($context)); исходное вычисление при этом остаётся активным.

Повторная финализация — ошибка жизненного циклаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'report');
$saved = hash_final($context);
try { hash_final($context); }
catch (TypeError $error) { echo "Повторная финализация отклонена.\n"; }
echo "Ранее сохранённый отпечаток report: ", $saved, "\n";
Ожидаемый результат
Повторная финализация отклонена.
Ранее сохранённый отпечаток report: 845e91831319e89c4d656bdb80c278ac09a7230d61e5dfd2e1b1fbb436ac8917

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Ошибки и ограничения. С 7.2 принимает объект. После final повторные hash_update/hash_final/hash_copy дают TypeError в текущем PHP, а прямой clone такого объекта — ValueError. Проверка instanceof HashContext не доказывает активность; состояние должно контролироваться вашим порядком вызовов.

Продолжить: hash_init, hash_copy, hash_update. Как выбрать задачу и алгоритм.

hash_copyОтветвить независимое вычисление после общего префикса.

PHP 5.3+ · hash

hash_copy(HashContext $context): HashContext

Ответвить два отчёта от общего заголовка. copy создаёт независимое состояние. Обычное $second = $first сохранило бы ссылку на тот же объект. Здесь заголовок один, а количество у двух продолжений разное.

Путь конкретных данных
  1. Исходный контекст содержит order=42 LF
  2. copy ответвляет независимое состояние
  3. Ветки получают qty=2 LF и qty=9 LF → разные отпечатки

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Ответвить независимое вычисление после общего префикса.

Параметр / результатКонтракт
contextАктивный контекст, который нужно скопировать.
returnДругой объект с тем же текущим состоянием и режимом; дальнейшие update/final независимы.
Ответвить два отчёта от общего заголовкаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, "order=42\n");
$other = hash_copy($context);
hash_update($context, "qty=2\n");
hash_update($other, "qty=9\n");
echo 'Количество2: ', hash_final($context), "\n";
echo 'Количество9: ', hash_final($other), "\n";
Ожидаемый результат
Количество2: c061afdd8db7ecdf0b1ed01d735f3aebadc4d8f6e0d0cb42b6de4d41d3aad987
Количество9: 42ff9f4045168a6d0e5c6118703aa77232fface963bc464179e112d7e9894022

Каждый final закрывает свою ветвь. Для двух коротких строк обычный hash проще; выгода копирования возникает, если общий префикс действительно дорог в повторной обработке.

Обычное присваивание $second = $base указывало бы на тот же объект. Копия HMAC также содержит секретное рабочее состояние: не экспортируйте её и не считайте безопасным логом. Польза копирования зависит от длины общего префикса; для двух коротких строк отдельный hash_hmac проще.

Подписать два сообщения с общим префиксомСамостоятельный пример
<?php
declare(strict_types=1);

$key = 'demo-only-key';
$base = hash_init('sha256', HASH_HMAC, $key);
hash_update($base, 'shop:v1:');
$first = hash_copy($base);
$second = hash_copy($base);
hash_update($first, 'order:42');
hash_update($second, 'order:43');
var_dump(hash_final($first) === hash_hmac('sha256', 'shop:v1:order:42', $key));
var_dump(hash_final($second) === hash_hmac('sha256', 'shop:v1:order:43', $key));
Ожидаемый результат
bool(true)
bool(true)

Ошибки и ограничения. Доступно с 5.3, с 7.2 работает с HashContext. Невалидный/финализированный context даёт TypeError; невозможность внутреннего копирования — Error. Native clone активного HashContext тоже копирует состояние, но явный hash_copy легче узнаётся в потоковом коде.

Продолжить: hash_final, hash_init, HashContext::__serialize. Как выбрать задачу и алгоритм.

hash_update_streamДобавить байты из уже открытого потока, начиная с его текущей позиции.

PHP 5.1.2+ · hash

hash_update_stream(HashContext $context, resource $stream, int $length = -1): int

Прочитать ровно полезную часть пакета. packet.txt содержит HEADabcTAIL. fopen с rb открывает байты без преобразования строк, fseek4 пропускает четыре байта заголовка, length3 ограничивает чтение содержимым abc.

Путь конкретных данных
  1. packet.txt: HEADabcTAIL; fseek4 пропускает HEAD
  2. update_stream читает ровно3байта abc
  3. final выдаёт SHA256 abc; в потоке остался TAIL

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Добавить байты из уже открытого потока, начиная с его текущей позиции.

Параметр / результатКонтракт
contextАктивный HashContext.
streamОткрытый читаемый stream resource, например fopen. Он не закрывается и не перематывается этой функцией.
lengthМаксимум байтов, не Unicode-знаков. -1 означает читать до конца; 0 — не добавлять ничего.
returnФактическое число добавленных байтов. Может быть меньше лимита при EOF/остановке чтения; 0 не доказывает успешность всего файла.
Прочитать ровно полезную часть пакетаСамостоятельный пример
<?php
declare(strict_types=1);

$stream = fopen(__DIR__ . '/packet.txt', 'rb');
if ($stream === false) { throw new RuntimeException('Пакет недоступен'); }
try {
    if (fseek($stream, 4) !== 0) { throw new RuntimeException('Не удалось пропустить HEAD'); }
    $context = hash_init('sha256');
    if (hash_update_stream($context, $stream, 3) !== 3) { throw new RuntimeException('Пакет обрезан'); }
    echo 'Поле abc: ', hash_final($context), "\n";
    echo 'Остаток потока: ', stream_get_contents($stream), "\n";
} finally { fclose($stream); }
Ожидаемый результат
Поле abc: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Остаток потока: TAIL

Функция сдвинула курсор, но не закрыла поток. finally гарантирует наше закрытие при обычном исключении. Лимит измеряется байтами, не буквами; EOF до трёх байтов означает неполный пакет.

Контракт ожидал ровно пять байтов; три уже изменили context, поэтому ошибку нельзя исправить простым повтором всего файла в тот же контекст. Начинают новый расчёт после решения причины сбоя. Для чтения до EOF проверка зависит от известного размера и типа потока.

Не принять усечённый пакет за полныйСамостоятельный пример
<?php
declare(strict_types=1);

$stream = fopen(__DIR__ . '/short.txt', 'rb');
if ($stream === false) { throw new RuntimeException('Open failed'); }
try {
    $context = hash_init('sha256');
    $added = hash_update_stream($context, $stream, 5);
    echo $added, "\n";
    if ($added !== 5) { echo 'incomplete: discard context'; }
    else { echo hash_final($context); }
} finally { fclose($stream); }
Ожидаемый результат
3
incomplete: discard context

Ошибки и ограничения. Неверный/закрытый stream resource и завершённый context приводят к TypeError. Ошибка чтения может дать warning и короткое число вместо исключения; возврат не равен гарантии целостности источника. С 7.2 context — объект. Для локальных бинарных файлов открывайте rb: текстовый режим может изменять байты на отдельных платформах.

Продолжить: hash_update_file, hash_file, hash_final. Как выбрать задачу и алгоритм.

hash_update_fileДобавить весь файл к уже накопленному префиксу вычисления.

PHP 5.1.2+ · hash

hash_update_file(HashContext $context, string $filename, ?resource $stream_context = null): bool

Добавить файл после версии формата. Первый аргумент — уже открытый HashContext, второй — имя файла. Это другой объект, чем stream_context с настройками чтения. Возвращаемый bool необходимо проверить до final.

Путь конкретных данных
  1. Контекст содержит format:v1 LF
  2. update_file добавляет весь export.txt с начала
  3. После успешного чтения final выдаёт digest версии и файла

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Добавить весь файл к уже накопленному префиксу вычисления.

Параметр / результатКонтракт
contextАктивный HashContext, уже содержащий ноль или больше байтов.
filenameЛокальный путь либо разрешённый fopen wrapper. Файл открывается с начала в бинарном режиме и закрывается функцией.
stream_contextОпциональный контекст stream_context_create; это настройки чтения, а не HashContext. null поддерживается с 8.0.
returntrue при полном успешном чтении; false при отказе открытия/чтения. Состояние могло получить часть данных до сбоя.
Добавить файл после версии форматаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, "format:v1\n");
if (!hash_update_file($context, __DIR__ . '/export.txt')) { throw new RuntimeException('Отчёт не прочитан; результат не выдаём'); }
echo 'Версия и файл: ', hash_final($context), "\n";
Ожидаемый результат
Версия и файл: 075dcceb6e9f5aedd33f9fff27a41605f9e9021e53cf5db7b5835e3437d5c25c

Архив содержит export.txt с обеими строками. При сбое чтения часть файла уже могла попасть в контекст: нельзя выдать хеш только префикса как полный отчёт. Для нового повтора создают новый контекст.

Не финализируем только заголовок и не выдаём его за сумму полного документа. При частичном сбое нет автоматического rollback: новый повтор — новый контекст. Обработчик warning временный и нужен только для детерминированной демонстрации.

Отказ чтения отменяет результат всего документаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'format-v1:');
$warnings = 0;
set_error_handler(function (int $level) use (&$warnings): bool {
    if ($level !== E_WARNING) { return false; }
    ++$warnings; return true;
});
try { $ok = hash_update_file($context, __DIR__ . '/missing.txt'); }
finally { restore_error_handler(); }
if (!$ok) { unset($context); echo 'discarded', "\n"; }
var_dump($warnings > 0);
Ожидаемый результат
discarded
bool(true)

Ошибки и ограничения. Отказ I/O — warning/false; неверный context или stream_context — TypeError; NUL в filename — ValueError. Поддержка wrappers не разрешает принимать произвольный пользовательский URL. Если нужен один файл без префикса, hash_file короче и понятнее.

Продолжить: hash_file, hash_update_stream, hash_update, hash_final. Как выбрать задачу и алгоритм.

hash_hmac_algosПолучить список алгоритмов, для которых PHP реализует HMAC.

PHP 7.2+ · hash

hash_hmac_algos(): array

Проверить алгоритм перед проверкой экспорта. Список hash_hmac_algos содержит алгоритмы, допустимые именно для HMAC. Наличие имени не означает, что его разрешила политика сервиса. Выбираем sha256 явно и отказываем при отсутствии.

Путь конкретных данных
  1. Протокол требует HMAC-SHA256
  2. hash_hmac_algos проверяет доступность sha256
  3. MAC вычисляется по order=42 с учебным ключом

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Получить список алгоритмов, для которых PHP реализует HMAC.

Параметр / результатКонтракт
ПараметрыНет.
returnИндексированный массив имён, допустимых для HMAC. В нём могут быть исторические алгоритмы; это не политика выбора.
Проверить алгоритм перед проверкой экспортаСамостоятельный пример
<?php
declare(strict_types=1);

if (!in_array('sha256', hash_hmac_algos(), true)) { throw new RuntimeException('Нет обязательного sha256'); }
echo "Проверка экспорта: sha256 доступен.\n";
echo 'Контрольный результат: ', hash_hmac('sha256', 'order=42', 'local-demo-key'), "\n";
Ожидаемый результат
Проверка экспорта: sha256 доступен.
Контрольный результат: 656a184e13c773994fcbc3e7a6030a261700ca9545352c0fb7142f52352e2e1b

Фиксированный ключ здесь только для известного тестового вектора; настоящие ключи хранят закрыто и генерируют случайно. Алгоритм из запроса нельзя принимать только потому, что он есть в этом списке.

Проверка происходит при настройке сервиса. Не переключаемся с неизвестного алгоритма на MD5 для «совместимости» и не берём алгоритм из незащищённого заголовка без протокольного правила.

Отказать при неподходящей конфигурацииСамостоятельный пример
<?php
declare(strict_types=1);

$configured = 'sha256';
$policy = ['sha256', 'sha512'];
if (!in_array($configured, $policy, true)
    || !in_array($configured, hash_hmac_algos(), true)) {
    throw new RuntimeException('Unsupported configured MAC');
}
echo 'MAC configuration accepted';
Ожидаемый результат
MAC configuration accepted

Ошибки и ограничения. Функция появилась в 7.2; раньше отдельного списка не было. Порядок и полный набор могут меняться между версиями. То, что MD5/SHA-1 исторически присутствует, не делает их разумным выбором нового протокола.

Продолжить: hash_algos, hash_hmac, hash_hkdf. Как выбрать задачу и алгоритм.

hash_hmac_fileВычислить MAC содержимого файла с известным обеим сторонам ключом.

PHP 5.1.2+ · hash

hash_hmac_file(string $algo, string $filename, string $key, bool $binary = false): string|false

Принять подлинную учебную запись и отклонить изменение. HMAC вычисляет проверочную строку из точных байтов и отдельного общего ключа. Ключ — третий аргумент; дописать его к тексту и вызвать hash означало бы другую конструкцию. В опыте отправитель и получатель локальные, ключ намеренно учебный; вывод MAC здесь воспроизводим, реальный ключ не публикуют.

Путь конкретных данных
  1. export.txt содержит заказ42 и количество2
  2. hmac_file читает файл с учебным общим ключом
  3. Сравнение подтверждает оригинал и отклоняет другой текст

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Вычислить MAC содержимого файла с известным обеим сторонам ключом.

Параметр / результатКонтракт
algoПоддерживаемый криптографический алгоритм HMAC.
filenameПуть или fopen wrapper; в примерах контролируемый локальный файл.
keyОбщий секретный ключ, не пароль пользователя и не имя файла.
binaryfalse — hex; true — двоичные байты для протокольного кодирования.
returnMAC string либо false при ошибке чтения. Имя файла и метаданные в MAC автоматически не включаются.
Принять подлинную учебную запись и отклонить изменениеСамостоятельный пример
<?php
declare(strict_types=1);

$known = hash_hmac_file('sha256', __DIR__ . '/export.txt', 'local-demo-key');
if ($known === false) { throw new RuntimeException('Файл не прочитан'); }
$received = $known;
echo 'Подпись: ', $known, "\n";
echo hash_equals($known, $received) ? "Исходная запись принята.\n" : "Подпись не совпала.\n";
$changed = hash_hmac('sha256', 'order=43', 'local-demo-key');
echo hash_equals($known, $changed) ? "Изменение принято.\n" : "Изменённая запись отклонена.\n";
Ожидаемый результат
Подпись: d9bcbf1b3f2fdb7cf18f3a4ca777e268b1a101282c62de32d548acd5d1c43444
Исходная запись принята.
Изменённая запись отклонена.

HMAC не скрывает текст и не предотвращает повтор того же сообщения. При протоколе с событиями добавляют аутентифицированные время/ID и атомарный учёт повторов. Для Base64 вызывайте hash_hmac с true, затем кодируйте сырые байты, а не hex.

Подписано количество 2, а fixture содержит 3. Реальный ожидаемый MAC принимают в определённом формате и проверяют его тип/длину; файл фиксируют от изменения до использования. HMAC не запрещает повторно импортировать тот же корректный файл.

Отвергнуть изменённую выгрузку до разбораСамостоятельный пример
<?php
declare(strict_types=1);

$key = 'demo-only-key';
$expected = hash_hmac('sha256', "id,qty\n42,2\n", $key, true);
$actual = hash_hmac_file('sha256', __DIR__ . '/changed.csv', $key, true);
if ($actual === false) { throw new RuntimeException('Read failed'); }
echo hash_equals($expected, $actual) ? 'import allowed' : 'reject before import';
Ожидаемый результат
reject before import

Ошибки и ограничения. Неизвестный/некриптографический algo — ValueError (современный контракт с 8.0); некриптографические HMAC запрещены с 7.2. Отказ I/O — warning/false; NUL в пути — ValueError. Не принимайте произвольные wrappers из запроса. Дополнительных algorithm options/stream_context в этой сигнатуре нет.

Продолжить: hash_hmac, hash_equals, hash_update_file, hash_init. Как выбрать задачу и алгоритм.

hash_hkdfПолучить отдельный ключ для конкретной роли из исходного ключевого материала.

PHP 7.1.2+ · hash

hash_hkdf(string $algo, string $key, int $length = 0, string $info = "", string $salt = ""): string

Разделить ключи двух назначений. HKDF получает уже стойкий исходный ключ, длину результата в байтах, строку назначения info и salt. Это не медленная проверка человеческого пароля. Разные info отделяют подпись выгрузки от уведомления.

Путь конкретных данных
  1. Учебный master M×32 и demo-salt
  2. HKDF с info shop/export/v1 и shop/notify/v1 даёт разные ключи
  3. Подпись экспорта не принимается ключом уведомления

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Получить отдельный ключ для конкретной роли из исходного ключевого материала.

Параметр / результатКонтракт
algoКриптографический алгоритм из hash_hmac_algos.
keyНепустой исходный ключевой материал в байтах; для примера — фиксированный fixture, для сервиса качественный секрет.
lengthДлина результата в байтах; 0 — длина digest выбранного алгоритма. Максимум 255×digest_size: для SHA-256 — 8160 байтов.
infoПубличный однозначный контекст назначения: например shop/export-signing/v1. Он отделяет роли и должен быть независим от значения key.
saltНеобязательная соль для стадии извлечения; пустая разрешена. Соль обычно не секретна, но её происхождение/хранение должны соответствовать протоколу.
returnВсегда двоичная строка производного ключа. Параметра binary и встроенного hex-режима нет.
Разделить ключи двух назначенийСамостоятельный пример
<?php
declare(strict_types=1);

$master = str_repeat('M', 32); // Only a reproducible local fixture.
$exportKey = hash_hkdf('sha256', $master, 32, 'shop/export/v1', 'demo-salt');
$notifyKey = hash_hkdf('sha256', $master, 32, 'shop/notify/v1', 'demo-salt');
$signature = hash_hmac('sha256', 'order=42', $exportKey);
echo hash_equals($signature, hash_hmac('sha256', 'order=42', $exportKey)) ? "Экспорт: подпись принята.\n" : "Экспорт: отказ.\n";
echo hash_equals($signature, hash_hmac('sha256', 'order=42', $notifyKey)) ? "Уведомление: подпись принята.\n" : "Уведомление: чужое назначение отклонено.\n";
Ожидаемый результат
Экспорт: подпись принята.
Уведомление: чужое назначение отклонено.

Повторяемое M×32 нужно только для теста. Реальный master берут из CSPRNG/системы ключей; одинаковые master/salt/info/length/algo восстанавливают одинаковые байты. Утечка master раскрывает производные ключи; info не является секретом.

Общее корневое значение не означает один ключ на все роли. Случайная соль от random_bytes должна сохраняться/передаваться по протоколу; генерация новой соли при каждой проверке изменила бы результат. Фиксированные разные info поддерживают разделение назначений; компрометация master компрометирует производные ключи.

Отделить подпись экспорта от подписи уведомленийСамостоятельный пример
<?php
declare(strict_types=1);

$master = str_repeat('M', 32);
$salt = 'demo-salt';
$exportKey = hash_hkdf('sha256', $master, 32, 'shop/export/v1', $salt);
$notifyKey = hash_hkdf('sha256', $master, 32, 'shop/notify/v1', $salt);
echo $exportKey !== $notifyKey ? "Ключи экспорта и уведомления разделены.\n" : "Назначения получили один ключ.\n";
$exportMac = hash_hmac('sha256', 'id=42', $exportKey);
$notifyMac = hash_hmac('sha256', 'id=42', $notifyKey);
echo hash_equals($exportMac, $notifyMac) ? "Подпись другого назначения принята.\n" : "Подпись другого назначения отклонена.\n";
Ожидаемый результат
Ключи экспорта и уведомления разделены.
Подпись другого назначения отклонена.

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Размер ограничивается digest_size×255, а не длиной текстового hex. Ключ/вывод не печатаем в рабочей диагностике: достаточно категории отказа.

Проверить пустой вход и недопустимый размерСамостоятельный пример
<?php
declare(strict_types=1);

foreach ([['', 32], ['demo', -1], ['demo', 8161]] as [$key, $length]) {
    try { hash_hkdf('sha256', $key, $length); }
    catch (ValueError $error) { echo 'rejected', "\n"; }
}
Ожидаемый результат
rejected
rejected
rejected

Ошибки и ограничения. Появилась в 7.1.2. С 8.0 ValueError при пустом key, неизвестном/некриптографическом algo, отрицательном или слишком большом length; раньше warning/false. HKDF концентрирует имеющуюся энтропию, не создаёт её из предсказуемого секрета. Не заменяет протокольный обмен ключами.

Продолжить: hash_pbkdf2, hash_hmac, hash_hmac_algos. Как выбрать задачу и алгоритм.

hash_pbkdf2Воспроизвести согласованное PBKDF2-выведение ключа из пароля.

PHP 5.5+ · hash

hash_pbkdf2(string $algo, string $password, string $salt, int $iterations, int $length = 0, bool $binary = false, array $options = []): string

Восстановить ключ известного старого формата. Это низкоуровневое получение ключа из фразы: алгоритм, пароль, соль, итерации, длина, binary. При true длина означает байты; при false — hex-знаки. Для новых аккаунтов PHP предоставляет password_hash.

Путь конкретных данных
  1. Учебные password/salt,2итерации,16байтов
  2. PBKDF2-SHA256 формирует известный тестовый вектор
  3. Hex ae4d… подтверждает совместимость параметров

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Воспроизвести согласованное PBKDF2-выведение ключа из пароля.

Параметр / результатКонтракт
algoКриптографический HMAC-алгоритм, например sha256. Некриптографические варианты запрещены.
passwordБайты пароля/секретной фразы. PHP не нормализует Unicode автоматически; формат должен быть согласован.
saltСоль; для реального нового пароля/материала обычно генерируют random_bytes, сохраняют отдельно от секрета. Не секретна.
iterationsПоложительное число внутренних раундов; влияет на стоимость вычисления. Значение задаёт доверенная политика/формат.
lengthДлина ВЫХОДНОЙ строки: при binary=true байты, при false hex-знаки. 0 — полный размер digest, соответственно 32/64 для SHA-256. Нечётная hex-длина допустима, но требует точного внешнего контракта.
binaryfalse — нижний hex; true — двоичный производный ключ.
optionsДополнительные настройки алгоритма; для допустимого SHA-256 оставьте []. Murmur seed неприменим, потому что Murmur не разрешён PBKDF2.
returnСтрока производного ключа; алгоритм/соль/iterations/формат не упакованы в неё автоматически.
Восстановить ключ известного старого форматаСамостоятельный пример
<?php
declare(strict_types=1);

$key = hash_pbkdf2('sha256', 'password', 'salt', 2, 16, true);
echo 'Тестовый ключ в hex: ', bin2hex($key), "\n";
$expected = hex2bin('ae4d0c95af6b46d32d0adff928f06dd0');
echo hash_equals($expected, $key) ? "Формат восстановлен; совместимость подтверждена.\n" : "Формат несовместим.\n";
Ожидаемый результат
Тестовый ключ в hex: ae4d0c95af6b46d32d0adff928f06dd0
Формат восстановлен; совместимость подтверждена.

Две итерации и опубликованная соль — исключительно короткий тестовый вектор, не стойкая настройка. Для реальных ключей параметры выбирают по стандарту протокола и бюджету; недоверенный файл не должен задавать неограниченное число итераций/байтов.

Метаданные здесь заданы программой; недоверенный файл нельзя бесконтрольно пускать в derivation. До вызова проверяют допустимый алгоритм, диапазон iterations/length, длину соли и общий бюджет работы. Иначе запрос может занять CPU/память. Не сохраняйте секретную фразу вместе с этими параметрами.

Сохранить параметры формата отдельно от ключаСамостоятельный пример
<?php
declare(strict_types=1);

$format = ['algo' => 'sha256', 'iterations' => 2,
    'length' => 16, 'salt_hex' => bin2hex('fixture-salt')];
$salt = hex2bin($format['salt_hex']);
if ($salt === false) { throw new RuntimeException('Bad fixture salt'); }
$key = hash_pbkdf2($format['algo'], 'demo phrase', $salt,
    $format['iterations'], $format['length'], true);
$again = hash_pbkdf2($format['algo'], 'demo phrase', $salt,
    $format['iterations'], $format['length'], true);
echo "Метаданные формата: sha256,2раунда,16байтов, соль fixture-salt.\n";
echo hash_equals($key, $again) ? "По тем же метаданным восстановлен тот же ключ.\n" : "Ключи различаются.\n";
Ожидаемый результат
Метаданные формата: sha256,2раунда,16байтов, соль fixture-salt.
По тем же метаданным восстановлен тот же ключ.

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Положительность — только минимальное условие PHP, не проверка достаточной стойкости. Стоимость растёт с iterations и количеством выходных блоков; измерение и ограничение частоты входа остаются задачей приложения.

Не разрешать нулевую стоимость или отрицательную длинуСамостоятельный пример
<?php
declare(strict_types=1);

foreach ([[0, 16], [2, -1]] as [$rounds, $length]) {
    try { hash_pbkdf2('sha256', 'demo', 'salt', $rounds, $length, true); }
    catch (ValueError $error) { echo 'rejected', "\n"; }
}
Ожидаемый результат
rejected
rejected

Ошибки и ограничения. Функция с 5.5; запрет некриптографических алгоритмов с 7.2; с 8.0 ошибки дают ValueError: неподдерживаемый algo, iterations≤0, length<0, соль длиннее INT_MAX−4. Последнее — предел C int, не PHP_INT_MAX на 64-bit; огромную соль нельзя выделять ради теста. options добавлен в 8.3 (сверено по PHP stub 8.2/8.3); для базы PHP 8.2 достаточно первых шести аргументов. Неизвестные типы password/salt и других аргументов при strict_types=1 дают TypeError.

Продолжить: hash_hkdf, hash_hmac_algos, hash_equals. Как выбрать задачу и алгоритм.

HashContext::__constructПонять, почему контекст создают hash_init, а не через new.

PHP 7.2+ · hash

private HashContext::__construct()

Управлять состоянием вычисления заказа. Конструктор private: new HashContext недоступен. Рабочий объект создаёт фабрика hash_init, которая выбирает алгоритм и инициализирует состояние.

Путь конкретных данных
  1. Известные байты примера или приложенного файла
  2. Вызов HashContext::__construct получает эти байты/состояние
  3. Результат: Прямое создание закрыто; используем hash_init.

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Понять, почему контекст создают hash_init, а не через new.

Параметр / результатКонтракт
ПараметрыНет. Конструктор закрыт для пользовательского кода.
returnКонструктор не возвращает значение; прямое создание недоступно. Рабочий объект возвращает hash_init.
Управлять состоянием вычисления заказаСамостоятельный пример
<?php
declare(strict_types=1);

try { new HashContext(); } catch (Error) { echo "Прямое создание закрыто; используем hash_init.\n"; }
$context = hash_init('sha256');
hash_update($context, 'order=42');
echo 'Отчёт: ', hash_final($context), "\n";
Ожидаемый результат
Прямое создание закрыто; используем hash_init.
Отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Состояние может содержать исходные байты, поэтому не передавайте его посетителю и не помещайте в публичные журналы. Формат сериализации не обещает переносимость между версиями/архитектурами. Для обычного готового файла проще повторить hash_file; для ветвления в памяти — hash_copy.

Фабрика — обычная функция, которая возвращает подготовленный объект. Здесь два вызова дают два разных состояния. У HashContext нельзя сделать подкласс: класс final, его собственную реализацию расширяют композицией, если это действительно нужно.

Создать два независимых документаСамостоятельный пример
<?php
declare(strict_types=1);

$first = hash_init('sha256');
$second = hash_init('sha256');
hash_update($first, 'first');
hash_update($second, 'second');
echo $first !== $second ? "Созданы независимые состояния двух документов.\n" : "Использовано одно состояние.\n";
echo "Документ first: ", hash_final($first), "\n";
echo "Документ second: ", hash_final($second), "\n";
Ожидаемый результат
Созданы независимые состояния двух документов.
Документ first: a7937b64b8caa58f03721bb6bacf5c78cb235febe0e70b1b84cd99541461a08e
Документ second: 16367aacb67a4a017c8da8ab95682ccb390863780f7114dda0a0e0c55644c7c4

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Ошибки и ограничения. HashContext появился в PHP 7.2 вместо resource. Прямой вызов private-конструктора даёт Error. Нельзя обойти жизненный цикл ради пустого контейнера и затем работать с ним как с корректным хешем.

Продолжить: hash_init, hash_copy. Как выбрать задачу и алгоритм.

HashContext::__debugInfoУвидеть алгоритм контекста в диагностическом выводе.

PHP 8.4+ · hash

HashContext::__debugInfo(): array

Управлять состоянием вычисления заказа. В PHP8.4+ диагностический метод возвращает поле algo. Стрелка вызывает метод именно этого объекта. Диагностика не завершает вычисление и не даёт хеш исходных данных.

Путь конкретных данных
  1. Известные байты примера или приложенного файла
  2. Вызов HashContext::__debugInfo получает эти байты/состояние
  3. Результат: Выбран алгоритм: sha256

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Увидеть алгоритм контекста в диагностическом выводе.

Параметр / результатКонтракт
ПараметрыНет. var_dump вызывает hook при выводе объекта; метод не предназначен для управления вычислением.
returnАссоциативный массив диагностики с ключом algo и именем алгоритма. Это не digest, не ключ и не все внутренние поля.
Управлять состоянием вычисления заказаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'order=');
$info = $context->__debugInfo();
echo 'Выбран алгоритм: ', $info['algo'], "\n";
hash_update($context, '42');
echo 'Отчёт: ', hash_final($context), "\n";
Ожидаемый результат
Выбран алгоритм: sha256
Отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Состояние может содержать исходные байты, поэтому не передавайте его посетителю и не помещайте в публичные журналы. Формат сериализации не обещает переносимость между версиями/архитектурами. Для обычного готового файла проще повторить hash_file; для ветвления в памяти — hash_copy.

Прямой вызов сделан только для изучения возвращаемого массива hook. В прикладном коде алгоритм хранится в собственной конфигурации; не строят протокол на debug-выводе. Стрелка -> вызывает метод конкретного объекта; результат — обычный массив.

Диагностика не закрывает вычислениеСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'a');
$info = $context->__debugInfo();
echo $info['algo'], "\n";
hash_update($context, 'bc');
echo "После диагностики продолжен abc: ", hash_final($context), "\n";
Ожидаемый результат
sha256
После диагностики продолжен abc: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Ошибки и ограничения. Метод добавлен в 8.4: на 8.2/8.3 этого API нет. Не использовать debug-массив для проверки, завершён ли context: он не обещает такого поля. Не выводить ключи/сырые сообщения вручную рядом с безопасным полем algo.

Продолжить: hash_init, hash_final. Как выбрать задачу и алгоритм.

HashContext::__serializeСохранить поддерживаемое активное состояние обычного хеширования.

PHP 8.0+ · hash

HashContext::__serialize(): array

Управлять состоянием вычисления заказа. serialize вызывает __serialize и получает поддерживаемое состояние незавершённого расчёта. Снимок в примере создаётся самим PHP из собственных байтов в том же процессе. HMAC-состояние экспортировать нельзя.

Путь конкретных данных
  1. Известные байты примера или приложенного файла
  2. Вызов HashContext::__serialize получает эти байты/состояние
  3. Результат: Продолженный отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Сохранить поддерживаемое активное состояние обычного хеширования.

Параметр / результатКонтракт
ПараметрыНет. Обычно PHP вызывает метод из serialize($context).
returnМассив внутреннего состояния для механизма сериализации. Это не строка digest; serialize вокруг него формирует отдельную строку представления объекта.
Управлять состоянием вычисления заказаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'order=');
$ownSnapshot = serialize($context);
$restored = unserialize($ownSnapshot, ['allowed_classes' => [HashContext::class]]);
if (!$restored instanceof HashContext) { throw new RuntimeException('Не восстановлен собственный снимок'); }
hash_update($restored, '42');
echo 'Продолженный отчёт: ', hash_final($restored), "\n";
hash_update($context, '43');
echo 'Независимый оригинал: ', hash_final($context), "\n";
Ожидаемый результат
Продолженный отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705
Независимый оригинал: 96df9bbc8108b8389a59245f6814791cb08630fe0d63cacd70fe5127ee635ea0

Состояние может содержать исходные байты, поэтому не передавайте его посетителю и не помещайте в публичные журналы. Формат сериализации не обещает переносимость между версиями/архитектурами. Для обычного готового файла проще повторить hash_file; для ветвления в памяти — hash_copy.

HMAC запрещён, чтобы не экспортировать секретное состояние. Финализация уже освободила рабочий буфер. Вместо сохранения результата вычисления храните сам возвращённый digest; для разветвления активного процесса используйте hash_copy.

Отказаться от экспорта HMAC и завершённого состоянияСамостоятельный пример
<?php
declare(strict_types=1);

$hmac = hash_init('sha256', HASH_HMAC, 'demo-only-key');
try { serialize($hmac); }
catch (Exception $error) { echo 'HMAC not serializable', "\n"; }
$finished = hash_init('sha256');
hash_final($finished);
try { serialize($finished); }
catch (Exception $error) { echo 'finished state rejected'; }
Ожидаемый результат
HMAC not serializable
finished state rejected

Ошибки и ограничения. Метод существует с 8.0. В PHP 8.5 сериализация HMAC, завершённого состояния SHA-256 или неподдерживаемого алгоритмом формата вызывает Exception. Внутренний массив включает алгоритм, режим, состояние, версию представления и свойства; схему не следует собирать вручную. Состояние может содержать буфер последних байтов исходного сообщения: не логируйте его и не передавайте клиенту.

Продолжить: HashContext::__unserialize, hash_copy, hash_final. Как выбрать задачу и алгоритм.

HashContext::__unserializeВосстановить собственный доверенный снимок HashContext для продолжения вычисления.

PHP 8.0+ · hash

HashContext::__unserialize(array $data): void

Управлять состоянием вычисления заказа. unserialize создаёт восстановленный объект и вызывает его __unserialize. allowed_classes ограничивает тип, но не делает чужие данные безопасными. Снимок в примере создаётся самим PHP из собственных байтов в том же процессе. HMAC-состояние экспортировать нельзя.

Путь конкретных данных
  1. Известные байты примера или приложенного файла
  2. Вызов HashContext::__unserialize получает эти байты/состояние
  3. Результат: Продолженный отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705

Распакуйте все файлы архива вместе. Команда php example.php выполняет этот пример; файлы из архива не нужно придумывать самостоятельно.

Восстановить собственный доверенный снимок HashContext для продолжения вычисления.

Параметр / результатКонтракт
dataВнутренний массив, полученный штатным __serialize. Обычно его передаёт unserialize, а не код приложения.
returnvoid: после успешного восстановления изменяется состояние объекта; отдельного digest не возвращается.
Управлять состоянием вычисления заказаСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'order=');
$ownSnapshot = serialize($context);
$restored = unserialize($ownSnapshot, ['allowed_classes' => [HashContext::class]]);
if (!$restored instanceof HashContext) { throw new RuntimeException('Не восстановлен собственный снимок'); }
hash_update($restored, '42');
echo 'Продолженный отчёт: ', hash_final($restored), "\n";
hash_update($context, '43');
echo 'Независимый оригинал: ', hash_final($context), "\n";
Ожидаемый результат
Продолженный отчёт: e7be97a0dece7cd8751b91338ce03d916b01ee32616d2b8b70e1807e5f307705
Независимый оригинал: 96df9bbc8108b8389a59245f6814791cb08630fe0d63cacd70fe5127ee635ea0

Состояние может содержать исходные байты, поэтому не передавайте его посетителю и не помещайте в публичные журналы. Формат сериализации не обещает переносимость между версиями/архитектурами. Для обычного готового файла проще повторить hash_file; для ветвления в памяти — hash_copy.

Учебный прямой вызов демонстрирует запрет: active object нельзя переинициализировать таким способом. Нормальный маршрут — unserialize доверенного снимка в другой объект. Не строим ручной обход private-конструктора через Reflection.

Не перезаписывать уже активный объектСамостоятельный пример
<?php
declare(strict_types=1);

$context = hash_init('sha256');
hash_update($context, 'ab');
$state = $context->__serialize();
try { $context->__unserialize($state); }
catch (Exception $error) { echo 'already initialized', "\n"; }
hash_update($context, 'c');
echo "Исходный контекст продолжил abc: ", hash_final($context), "\n";
Ожидаемый результат
already initialized
Исходный контекст продолжил abc: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

Вывод показывает результат описанного выше опыта с указанными исходными данными.

Ошибки и ограничения. С 8.0. Неверный тип data даёт TypeError; уже активный объект, повреждённая/неполная схема, неизвестный алгоритм, HMAC-режим или непригодное внутреннее состояние — Exception в текущей реализации. Не подавляйте эти ошибки и не принимайте snapshots извне. Формат не гарантированно переносим между PHP/архитектурами.

Продолжить: HashContext::__serialize, hash_init, hash_copy. Как выбрать задачу и алгоритм.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.