Александр Некрасов · Автономное руководство

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

Разделы этой страницы
  1. Bzip2: сжатые строки, файлы .bz2 и безопасное чтение выгрузки
  2. Отчёт меньше при передаче, прежние данные после распаковки
  3. Выбрать строку, поток или архив
  4. Установка, libbzip2 и проверка нужного PHP
  5. Открыть, прочитать или записать, завершить
  6. Три разных отказа: открыть путь, разобрать Bzip2, проверить документ
  7. Функция импорта: сначала проверить весь ограниченный документ
  8. Где расходуется время и память
  9. Упражнение: файл для скачивания и проверяемое восстановление
  10. Все функции Bzip2 по действиям
  11. Функции и методы этой темы
  12. Код и руководство с собой
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 105 · От основ к практике

Bzip2: сжатые строки, файлы .bz2 и безопасное чтение выгрузки

Полная ветка Bzip2: установка, все 10 функций, потоки, ошибки и ограничения импорта.

Отчёт меньше при передаче, прежние данные после распаковки

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

Из stock.csv.bz2 возвращаем строки остатков

Bzip2 меняет представление последовательности байтов. Разбор товаров начинается после корректной распаковки.

  1. CSV: SKU-7;2

    Исходные данные поставщика описывают товар и остаток.

  2. Bzip2-поток

    Сжатые байты сохраняют или передают без текстовых преобразований.

  3. Распаковка порциями

    bzread отдаёт куски, которые не обязаны совпадать со строками CSV; проверяем bzerror и лимиты.

  4. Разбор и проверка CSV

    После завершённого потока проверяем поля и только затем меняем данные приложения.

Непустой результат не доказывает целостность усечённого входа. bzdecompress может требовать много памяти; .tar.bz2 после распаковки всё ещё содержит TAR-контейнер.

Поставщик отдаёт выгрузку остатков в CSV, но передаёт файл stock.csv.bz2. Сначала нужно вернуть исходный текст, затем разбирать строки CSV. Bzip2 решает первый шаг: сжимает последовательность байтов без потери содержимого. Он не понимает товары, столбцы и SQL.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

Что проходит между поставщиком и импортом
  1. Текст: SKU-7;2
  2. Сжатие → двоичные байты Bzip2
  3. Передача или сохранение этих байтов
  4. Распаковка → снова SKU-7;2; затем разбор CSV
Самый короткий полный опытСамостоятельный пример
<?php
$packed = bzcompress('SKU-7;2');
$text = bzdecompress($packed);
echo $text, "\n";
Ожидаемый результат
SKU-7;2

Опыт использует заведомо исправные короткие данные и установленное расширение. Файл не создаётся: обе строки находятся в памяти. Следующие разделы добавляют обработку отказа и работу с файлом.

Аналогия — компактная упаковка одного рулона данных. Она не является паролем или шифрованием и не даёт права доверять содержимому после распаковки. Сжатие не обязано уменьшать короткие или уже сжатые данные.

Выбрать строку, поток или архив

ЗадачаИнструментГраница
Короткая строка уже в памятиbzcompress() / bzdecompress()Вход и весь выход находятся в памяти; у bzdecompress нет параметра максимума результата.
Один большой .csv.bz2bzopen() → bzread() → bzclose()Читаем порции распакованных байтов и проверяем ошибки. Размер порции не равен строке CSV.
Записать отчёт порциямиbzopen() → bzwrite() → bzclose()Передаём несжатые строки; w обнуляет прежний файл.
Несколько файлов в .tar.bz2Bzip2 сжимает TAR-контейнер; разбор TAR — отдельный этапРаспакованные байты здесь будут TAR, а не сразу CSV.
Файл .zip или .gzНужен обработчик соответствующего форматаИмена расширений не подтверждают содержимое. Установка ZIP не включает автоматически bz2.

Bzip2 не является универсальным транспортным форматом JSON/HTTP. При интеграции согласуйте с другой стороной точные байты и алгоритм; Base64 только превращает бинарные данные в текст и увеличивает размер. Не применяйте bzcompress к уже сжатому результату без специальной причины.

Установка, libbzip2 и проверка нужного PHP

В PHP это расширение bz2, а внешняя библиотека — libbzip2 версии не ниже 1.0.x. Поддержка не включается автоматически во всех сборках. При сборке PHP используют --with-bz2[=DIR]. В готовой системе выбирают пакет своего дистрибутива/версии PHP; на Windows — совместимую php_bz2.dll из соответствующей сборки, с совпадающими версией, архитектурой и TS/NTS.

В php.ini запись extension=bz2 подключает доступную библиотеку. CLI и сервер могут читать разные ini: команды php --ini, php -m, php --ri bz2 относятся к тому php, который найден в PATH. Composer проверяет окружение своего CLI, а не веб-страницу. После настройки веб-процессы перезапускают и проверяют тем же способом внутри нужного SAPI.

Проверка расширения
  1. Найти конкретный PHP и его ini
  2. extension_loaded("bz2") → включено ли расширение в этом процессе
  3. function_exists("bzopen") → доступен ли нужный API
  4. Только после этого выполнять примеры
Диагностика учебного окруженияСамостоятельный пример
<?php
var_dump(extension_loaded('bz2'));
var_dump(function_exists('bzopen'));
var_dump(in_array('compress.bzip2', stream_get_wrappers(), true));
Ожидаемый результат
bool(true)
bool(true)
bool(true)

Этот результат относится к PHP с явно подключённым bz2. in_array ищет строку среди имён обёрток; третий аргумент true включает строгое сравнение.

У расширения нет собственных php.ini-директив и экспортируемых PHP-констант. Обёртка compress.bzip2:// и фильтры bzip2.compress/bzip2.decompress предоставляют отдельные потоковые входы; их наличие не меняет контракт функций ниже. Что такое обёртка и поток. Динамическое подключение через dl в обработчике запроса здесь не требуется.

Проверка этой главы: PHP 8.5.11, libbzip2 1.0.8; DLL включалась только для тестового процесса через -d extension=bz2. Основные примеры подходят для PHP 8.2+, а тест новых ошибок bzcompress отдельно помечен 8.5+.

Открыть, прочитать или записать, завершить

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

Жизнь одного ресурса
  1. bzopen("report.bz2", "w") выдаёт указатель на поток
  2. bzwrite передаёт обычные порции текста
  3. bzclose завершает и закрывает поток
  4. Новый bzopen(..., "r") создаёт независимого читателя

У читающего ресурса есть позиция: каждое чтение продвигает её. У пишущего ресурса есть состояние компрессора: пока поток не завершён, файл нельзя считать готовой выгрузкой. bzflush() — исторический no-op; это не публикация готовых данных. bzclose() не удаляет файл, а освобождает ресурс.

Старые руководства называют возвращаемое значение указателем файла Bzip2. В PHP это resource, в текущей реализации — поток. Нельзя создавать его через new или передавать вместо него имя файла в bzread. Если bzopen получает уже открытый совместимый файловый поток, закрытие обёртки закрывает и исходный ресурс; это показано в примере владения ресурсом.

Три разных отказа: открыть путь, разобрать Bzip2, проверить документ

ЭтапПризнакДействие
Открытие путиbzopen вернул false или диагностировал неверный аргументНе вызывать bzerror(false); проверить путь, права, режим и исходный поток.
Распаковкаbzread вернул false либо bzerror сообщает ошибкуСохранить техническую причину, закрыть ресурс, отвергнуть документ.
Смысл выгрузкиBzip2 корректен, но нет нужной шапки/поля/типа/подписи источникаПроверить схему и договор источника до бизнес-изменений.

Числа ниже пришли из libbzip2. Они не объявлены в PHP как константы BZ_*. Диагностические функции возвращают код/строку для открытого ресурса; bzcompress и bzdecompress могут вернуть число непосредственно. Не каждый код достижим каждым PHP-вызовом.

Код / имя в библиотекеСмысл
0 — BZ_OKОшибка не зарегистрирована.
−1 — BZ_SEQUENCE_ERRORНеподходящая последовательность операций/состояние.
−2 — BZ_PARAM_ERRORНекорректный параметр низкоуровневого API; часть случаев PHP перехватывает исключениями.
−3 — BZ_MEM_ERRORБиблиотеке не хватило рабочей памяти.
−4 — BZ_DATA_ERRORПовреждены данные или контрольная сумма.
−5 — BZ_DATA_ERROR_MAGICНачало потока не соответствует Bzip2.
−6 — BZ_IO_ERRORОшибка ввода/вывода.
−7 — BZ_UNEXPECTED_EOFФайл закончился раньше завершения сжатого потока.
−8 — BZ_OUTBUFF_FULLНедостаточно выходного буфера у соответствующего библиотечного API.
−9 — BZ_CONFIG_ERRORНесовместимая конфигурация библиотеки.

В низкоуровневой библиотеке существуют и положительные состояния выполнения 1…4. Не переносите её протокол напрямую на PHP: например, в проверенном корректном завершении bzerror сообщает errno=0/errstr=OK, а не обязательное число 4.

Функция импорта: сначала проверить весь ограниченный документ

Теперь вход — путь к одному сжатому CSV. Мы хотим получить текст, только если чтение не выявило ошибки и распакованный объём не превысил 12 000 байт. Это компактная демонстрация политики «всё или отказ», поэтому хранить весь результат здесь допустимо. Для большого импорта выход складывают в служебный файл или промежуточное хранилище; бизнес-изменения подтверждают после проверки документа.

function readReport($path, $limit) объявляет операцию с двумя входами; return $text возвращает её результат. .= дописывает строку, while повторяет чтение, break завершает цикл. $error['errno'] — поле массива. Вызвавшая сторона ловит RuntimeException и не получает частичный отчёт. Параметры и return; Ключи массива.

Почему повреждённая выгрузка не доходит до импорта
  1. Открыть путь → ресурс
  2. Читать порцию → проверить bzerror и суммарный предел
  3. Только после конца без ошибки вернуть весь текст
  4. Проверить CSV-схему; затем отдельный этап базы
Отвергнуть повреждённый и слишком большой отчётСамостоятельный пример
<?php
function readReport($path, $limit) {
    if (!is_int($limit) || $limit < 1) {
        throw new InvalidArgumentException('Неверный лимит');
    }
    $handle = bzopen($path, 'r');
    if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
    $text = '';
    try {
        while (true) {
            $chunk = bzread($handle, 4096);
            $error = bzerror($handle);
            if ($chunk === false || $error['errno'] !== 0) {
                throw new RuntimeException('Повреждённый Bzip2');
            }
            if ($chunk === '') { break; }
            if (strlen($chunk) > $limit - strlen($text)) {
                throw new RuntimeException('Превышен лимит');
            }
            $text .= $chunk;
        }
        return $text;
    } finally { bzclose($handle); }
}
file_put_contents('valid.bz2', bzcompress("id;qty\n7;2\n"));
echo readReport('valid.bz2', 12000);
$large = bzcompress(str_repeat('A', 10000));
file_put_contents('cut.bz2', substr($large, 0, -10));
file_put_contents('large.bz2', $large);
foreach (['cut.bz2', 'large.bz2'] as $path) {
    try { readReport($path, 9000); }
    catch (RuntimeException $error) { echo $error->getMessage(), "\n"; }
}
Ожидаемый результат
id;qty
7;2
Повреждённый Bzip2
Превышен лимит

Оба отказа происходят до возврата текста. Это не универсальная проверка контейнера: добавленные после первого завершённого потока байты/второй поток этим API могут игнорироваться. Нужен согласованный формат и при необходимости доверенный размер/хеш исходного документа.

Договор этой функции: путь уже выбран приложением в контролируемом каталоге; это не произвольный URL пользователя. Она не реализует загрузку HTTP, таймаут, разбор CSV или транзакцию. Следующие этапы: проверка загруженного файла, разбор CSV, подтверждение импорта транзакцией. Сжатие не делает присланный SQL безопасным: импорт SQL выполняет команды, а не просто читает таблицу.

Где расходуется время и память

Сжатие и распаковка выполняются синхронно в текущем PHP-процессе. Строковые функции держат вход и выход в памяти; потоковое чтение ограничивает размер передаваемых приложению порций, но у библиотеки остаются внутренние блоки. Большая степень сжатия входа не гарантирует малый результат. Для больших выгрузок нужен фоновый обработчик, лимиты процесса и политика отмены; bzread сам не является планировщиком или таймаутом.

block_size у bzcompress — множитель размера одного блока: значение 1…9 означает 100 000…900 000 байт, а не количество блоков файла. Он влияет на память компрессора и распаковщика. work_factor определяет переключение алгоритма на повторяющихся данных, а не качество результата. У use_less_memory экономится внутренняя рабочая память распаковщика ценой времени; строка полного результата всё равно размещается в PHP. Фактическую стоимость измеряют на своих CSV, а не по имени режима.

Когда достаточноЧто выбрать
Маленький текст и известный пределbzcompress/bzdecompress с проверкой типов и договора целостности.
Большой файлbzopen/bzread/bzwrite, обработка порциями; отказаться от накопления всего результата.
Нужен немедленно читаемый журналОбычная потоковая запись или отдельные завершённые части, а не bzflush.
Контейнер из нескольких файловОтдельная работа с форматом контейнера и безопасными путями распаковки.

Упражнение: файл для скачивания и проверяемое восстановление

Сделайте файл с двумя строками статусов заказа, закройте поток и убедитесь, что чтение вернуло обе строки. Не вызывайте bzcompress перед bzwrite. Предложенный вариант проверяет объём записи и использует finally для освобождения ресурса.

Две строки одного отчёта
  1. order=7;paid=1 и order=9;paid=0
  2. Передать обе обычные строки через bzwrite
  3. Закрыть поток
  4. Прочитать и сравнить восстановленный текст
Вариант решенияСамостоятельный пример
<?php
$report = "order=7;paid=1\norder=9;paid=0\n";
$handle = bzopen('orders.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    if (bzwrite($handle, $report) !== strlen($report)) {
        throw new RuntimeException('Неполная запись');
    }
} finally { bzclose($handle); }
$restored = bzdecompress(file_get_contents('orders.bz2'));
var_dump($restored === $report);
echo $restored;
Ожидаемый результат
bool(true)
order=7;paid=1
order=9;paid=0

Такой небольшой круг записи/чтения полезен при настройке интеграции. Для выдачи браузеру отдельно задают заголовки и проверяют право доступа; бинарные байты не вставляют в HTML.

Усложнение: замените один байт входа или обрежьте файл и пропустите его через функцию из предыдущего раздела. Сформулируйте отдельно, что означает «файл прочитан», «Bzip2 не сообщил ошибку» и «поставщик прислал полный правильный документ». Это разные проверки.

Все функции Bzip2 по действиям

ДействиеФункции
Строки в памятиbzcompress(), bzdecompress()
Открыть и завершитьbzopen(), bzclose()
Порции байтовbzread(), bzwrite()
Диагностика ресурсаbzerrno(), bzerrstr(), bzerror()
Историческое ограничениеbzflush()

Каждая карточка ниже содержит полный договор параметров, результат и отказы, минимальный опыт, прикладной случай и связи. В официальном дереве Bzip2 нет отдельной страницы констант/INI-настроек; их отсутствие объяснено в разделе установки.

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

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

bzcompressУменьшить текст отчёта перед хранением или передачей. Результат — двоичная строка Bzip2, а не имя файла.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzcompress(string $data, int $block_size = 4, int $work_factor = 0): string|int

Уменьшить текст отчёта перед хранением или передачей. Результат — двоичная строка Bzip2, а не имя файла.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzcompress(string $data, int $block_size = 4, int $work_factor = 0): string|int

ПараметрДоговор
$dataИсходные байты: текст, CSV, JSON или другое содержимое. Функция не перекодирует текст.
$block_size1…9: блоки по 100 000 байт. Больше блок — больше рабочей памяти; 9 ориентирован на лучшее сжатие, но не обещает выигрыш для любого короткого входа.
$work_factor0…250: порог усилий перед запасным алгоритмом для повторяющихся данных. 0 означает библиотечное значение 30; это не уровень качества. При одном block_size результат не зависит от work_factor.

Результат и ошибки. Сжатая строка либо целое число ошибки. Проверяйте is_string. С PHP 8.5 неверные диапазоны block_size/work_factor вызывают ValueError; в прежних версиях возможен числовой отказ библиотеки. Неподходящий тип аргумента даёт TypeError.

Данные в первом примере
  1. Текст SKU-7;2
  2. bzcompress сжимает байты в строку
  3. bzdecompress восстанавливает исходный текст
Сжать и восстановить короткую строкуСамостоятельный пример
<?php
$packed = bzcompress('SKU-7;2');
var_dump(is_string($packed));
echo bzdecompress($packed), "\n";
Ожидаемый результат
bool(true)
SKU-7;2

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

Теперь вынесем упаковку повторяющегося отчёта в функцию: return возвращает строку вызывающему коду, а is_string отделяет её от кода ошибки. str_repeat повторяет учебную строку, strlen считает байты.

Общий упаковщик CSV-отчётаСамостоятельный пример
<?php
function packReport($text) {
    $packed = bzcompress($text, 9, 0);
    if (!is_string($packed)) {
        throw new RuntimeException('Ошибка сжатия: ' . $packed);
    }
    return $packed;
}
$csv = str_repeat("SKU-7;2\n", 100);
$packed = packReport($csv);
var_dump(strlen($packed) < strlen($csv));
var_dump(bzdecompress($packed) === $csv);
var_dump(bzcompress($csv, 4, 0) === bzcompress($csv, 4, 30));
Ожидаемый результат
bool(true)
bool(true)
bool(true)

Здесь проверены уменьшение именно этого набора и обратимость. Равенство вариантов work_factor не является замером скорости.

Обработать неправильную настройку на PHP 8.5+Самостоятельный пример
<?php
foreach ([[0, 0], [4, 251]] as [$block, $work]) {
    try { bzcompress('report', $block, $work); }
    catch (ValueError $error) { echo "Неверная настройка\n"; }
}
Ожидаемый результат
Неверная настройка
Неверная настройка

Этот случай требует PHP 8.5+: catch перехватывает ValueError. В прикладной форме диапазоны проверяют до вызова, а не делают ошибку обычной веткой управления.

Границы применения. Вход и выход одновременно занимают память; это не потоковая обработка большого файла. При больших выгрузках используйте bzopen/bzwrite. Не путайте Bzip2 с gzip, zlib и ZIP: принимающая сторона должна ожидать тот же формат.

Связанные действия: bzdecompress(), bzopen(), bzwrite().

bzdecompressВернуть исходные байты из небольшой сжатой строки, например из сохранённого отчёта.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzdecompress(string $data, bool $use_less_memory = false): string|int|false

Вернуть исходные байты из небольшой сжатой строки, например из сохранённого отчёта.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzdecompress(string $data, bool $use_less_memory = false): string|int|false

ПараметрДоговор
$dataСжатые байты Bzip2, а не путь к .bz2. Для пути используйте bzopen/bzread.
$use_less_memorytrue включает более экономный внутренний алгоритм. Тип bool с PHP 8.0; до этого int. Для больших блоков документация оценивает рабочую память примерно в 2300 КБ при примерно двукратном замедлении; это не предел всей памяти PHP.

Результат и ошибки. Строка, false или целый код ошибки. Пустая строка и "0" допустимы, поэтому if (!$result) неверно. Успешный тип сам по себе не доказывает полноту произвольного внешнего входа: поведение обрезанного потока разобрано ниже.

Данные в первом примере
  1. Bzip2-строка содержит отчёт "0"
  2. bzdecompress возвращает строку
  3. is_string подтверждает тип; echo показывает 0
Нулевой результат тоже является текстомСамостоятельный пример
<?php
$text = bzdecompress(bzcompress('0'));
var_dump(is_string($text));
echo $text, "\n";
Ожидаемый результат
bool(true)
0

Число ошибки и текст "0" отличаются типом. Проверка is_string сохраняет это различие.

Отказ формата и экономный режимСамостоятельный пример
<?php
$bad = bzdecompress('not a bzip2 stream');
echo is_string($bad) ? "Текст\n" : "Отказ\n";
$packed = bzcompress("id;total\n7;450\n");
var_dump(bzdecompress($packed, true) === bzdecompress($packed, false));
Ожидаемый результат
Отказ
bool(true)

Экономный режим меняет алгоритм, а не содержимое. Здесь нет утверждения о времени на другом компьютере.

Две независимые границы: PHP 8.5.11/libbzip2 1.0.8 возвращает только первый поток при склейке двух Bzip2-потоков; на данном обрезанном входе возвращает часть текста. Не переносите на этот вызов поведение утилиты bunzip2 для нескольких потоков.

Не принять часть выгрузки за полный отчётСамостоятельный пример
<?php
$joined = bzcompress('ONE') . bzcompress('TWO');
echo bzdecompress($joined), "\n";
$original = str_repeat('A', 10000);
$truncated = substr(bzcompress($original), 0, -10);
$result = bzdecompress($truncated);
var_dump(is_string($result));
var_dump(is_string($result) && strlen($result) < strlen($original));
Ожидаемый результат
ONE
bool(true)
bool(true)

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

Границы применения. Весь распакованный результат создаётся в памяти. У малого входа может быть большой выход. Функция не даёт лимита результата или универсальной проверки, что потреблены все входные байты; для внешнего импорта нужны ограничения и отдельный договор целостности.

Связанные действия: bzcompress(), bzopen(), bzread(), bzerror().

bzopenОткрыть файл .bz2 так, чтобы последующие чтения и записи работали с обычным текстом.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzopen(string|resource $file, string $mode): resource|false

Открыть файл .bz2 так, чтобы последующие чтения и записи работали с обычным текстом.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzopen(string|resource $file, string $mode): resource|false

ПараметрДоговор
$fileПуть к файлу или уже открытый поток, допускающий файловый дескриптор и совместимый режим. Не всякий поток в памяти можно так обернуть.
$modeТолько "r" для чтения или "w" для записи. "w" создаёт файл либо обнуляет существующий. Прямого режима добавления "a", "r+" или "rb" нет.

Результат и ошибки. Ресурс либо false, например при невозможности открыть путь; операция открытия может выдать warning. Ошибочный режим в PHP 8.5.11 вызывает ValueError. Неподходящий ресурс/режим исходного потока тоже проверяется. Не рассчитывайте, что все ошибки представляются только false.

Данные в первом примере
  1. report.bz2 открывается для записи
  2. bzwrite получает обычный REPORT
  3. bzclose завершает файл; новый режим r читает REPORT
Записать и заново открыть отчётСамостоятельный пример
<?php
$handle = bzopen('report.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
bzwrite($handle, 'REPORT');
bzclose($handle);
$handle = bzopen('report.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try { echo bzread($handle), "\n"; }
finally { bzclose($handle); }
Ожидаемый результат
REPORT

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

Передать уже открытый файловый потокСамостоятельный пример
<?php
file_put_contents('source.bz2', bzcompress('INVOICE'));
$raw = fopen('source.bz2', 'rb');
if ($raw === false) { throw new RuntimeException('Не открыт источник'); }
$compressed = bzopen($raw, 'r');
if ($compressed === false) { fclose($raw); throw new RuntimeException('Нет Bzip2-потока'); }
try { echo bzread($compressed), "\n"; }
finally { bzclose($compressed); }
var_dump(is_resource($raw));
Ожидаемый результат
INVOICE
bool(false)

Здесь rb относится к fopen, а r — к bzopen. В проверенной реализации закрытие обёртки закрывает и исходный поток: не вызывайте затем fclose ещё раз.

Границы применения. Переданный файловый поток должен стоять в начале нужного сжатого потока; предварительное чтение заголовка сдвигает позицию. Режим чтения файла не подтверждает корректность Bzip2: неверный формат часто обнаруживается только при bzread. Для потоковых обёрток см. схемы и контексты.

Связанные действия: bzread(), bzwrite(), bzclose(), bzerror().

bzreadПрочитать порцию распакованных байтов из файла, не собирая весь результат сразу.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzread(resource $bz, int $length = 1024): string|false

Прочитать порцию распакованных байтов из файла, не собирая весь результат сразу.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzread(resource $bz, int $length = 1024): string|false

ПараметрДоговор
$bzОткрытый ресурс bzopen в режиме r; позиция продвигается после чтения.
$lengthМаксимум запрошенных распакованных байтов, по умолчанию 1024; за один вызов не больше 8192. Результат может быть короче. 0 даёт пустую строку; отрицательное значение в PHP 8 вызывает ValueError.

Результат и ошибки. Строка либо false при ошибке. Конец корректного потока даёт пустую строку; сверяйте bzerror, поскольку EOF бывает также на повреждённом входе. Длина измеряется в байтах, граница порции может пройти посередине UTF-8 символа или CSV-строки.

Данные в первом примере
  1. Исходный текст ABCDEF
  2. bzread(..., 2) → AB и сдвиг позиции
  3. Следующий bzread(..., 2) → CD
Увидеть движение позицииСамостоятельный пример
<?php
file_put_contents('letters.bz2', bzcompress('ABCDEF'));
$handle = bzopen('letters.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    echo bzread($handle, 2), "\n";
    echo bzread($handle, 2), "\n";
} finally { bzclose($handle); }
Ожидаемый результат
AB
CD

Один и тот же ресурс помнит позицию. Два вызова не возвращают начало файла заново.

while (true) повторяет действия, break останавливает цикл; += увеличивает счётчик. Проверяем ошибку до использования порции. Циклы и выход.

Посчитать объём выгрузки с пределомСамостоятельный пример
<?php
file_put_contents('batch.bz2', bzcompress(str_repeat('A', 10000)));
$handle = bzopen('batch.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
$total = 0;
try {
    while (true) {
        $chunk = bzread($handle, 4096);
        $error = bzerror($handle);
        if ($chunk === false || $error['errno'] !== 0) {
            throw new RuntimeException('Ошибка Bzip2: ' . $error['errstr']);
        }
        if ($chunk === '') { break; }
        if (strlen($chunk) > 12000 - $total) {
            throw new RuntimeException('Лимит распаковки');
        }
        $total += strlen($chunk);
    }
} finally { bzclose($handle); }
echo $total, "\n";
Ожидаемый результат
10000

Счётчик не накапливает весь текст. Предел 12 000 относится к выходным байтам этого опыта; библиотека всё равно имеет внутренние буферы. Здесь ещё нет CSV-парсинга, записи в базу или таймаута.

Границы применения. Короткая порция сама по себе не доказывает конец. Для данных вне доверенного источника ограничивают также размер входа, длительность задачи и область записи; завершённые бизнес-изменения делают после проверки всего документа. feof без проверки ошибки недостаточен.

Связанные действия: bzopen(), bzerror(), bzclose().

bzwriteПередать обычные байты в открытый Bzip2-файл; сжатие выполняется внутри потока.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzwrite(resource $bz, string $data, ?int $length = null): int|false

Передать обычные байты в открытый Bzip2-файл; сжатие выполняется внутри потока.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzwrite(resource $bz, string $data, ?int $length = null): int|false

ПараметрДоговор
$bzПишущий Bzip2-ресурс от bzopen(..., "w").
$dataНесжатая строка. Предварительный bzcompress создаст двойное сжатие.
$lengthМаксимум байтов исходной строки: запись останавливается на length либо конце data — что наступит раньше. null означает всю строку; параметр nullable с PHP 8.0. Длина не является числом Unicode-символов или размером файла после сжатия. В реализации PHP 8.5.11 нулевая/отрицательная длина ничего не пишет и даёт 0.

Результат и ошибки. Количество принятых исходных байтов, либо false. Сравнивайте с длиной переданного фрагмента; 0 — не false и не означает успешную запись непустого фрагмента. Это алиас потоковой записи fwrite в реализации PHP.

Данные в первом примере
  1. ABCDEF — обычный текст
  2. bzwrite с length 3 принимает ABC
  3. После закрытия чтение даёт ABC
Записать только первые три байтаСамостоятельный пример
<?php
$handle = bzopen('prefix.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try { echo bzwrite($handle, 'ABCDEF', 3), "\n"; }
finally { bzclose($handle); }
echo bzdecompress(file_get_contents('prefix.bz2')), "\n";
Ожидаемый результат
3
ABC

В файл попадёт сжатое представление ABC, а не строка ABCDEF и не три сжатых байта.

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

Собрать выгрузку из нескольких строкСамостоятельный пример
<?php
$lines = ["id;qty\n", "7;2\n", "9;1\n"];
$handle = bzopen('stock.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    foreach ($lines as $line) {
        if (bzwrite($handle, $line) !== strlen($line)) {
            throw new RuntimeException('Неполная запись');
        }
    }
} finally { bzclose($handle); }
echo bzdecompress(file_get_contents('stock.bz2'));
Ожидаемый результат
id;qty
7;2
9;1

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

Границы применения. Закрытие завершает сжатый поток. Не открывайте заново тот же путь с w для каждой строки: старое содержимое будет обнулено. Не считайте bzflush способом сделать промежуточный файл завершённым.

Связанные действия: bzopen(), bzclose(), bzflush(), bzcompress().

bzcloseЗавершить работу с ресурсом; для пишущего потока закончить формирование .bz2.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzclose(resource $bz): bool

Завершить работу с ресурсом; для пишущего потока закончить формирование .bz2.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzclose(resource $bz): bool

ПараметрДоговор
$bzОткрытый ресурс Bzip2. После закрытия он больше не пригоден для чтения, записи или диагностики.

Результат и ошибки. true при успешном закрытии, false при отказе. Закрытый или неподходящий ресурс может вызвать TypeError. true не является доказательством физической сохранности диска или полноты исходного бизнес-документа.

Данные в первом примере
  1. Пишущий ресурс принимает READY
  2. bzclose завершает поток
  3. Файл можно открыть отдельным читателем
Завершить маленький файлСамостоятельный пример
<?php
$handle = bzopen('ready.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
bzwrite($handle, 'READY');
var_dump(bzclose($handle));
var_dump(is_resource($handle));
echo bzdecompress(file_get_contents('ready.bz2')), "\n";
Ожидаемый результат
bool(true)
bool(false)
READY

Файл остаётся на диске; закрывается ресурс, а не удаляется содержимое.

Освободить поток при отказе проверкиСамостоятельный пример
<?php
file_put_contents('report.bz2', bzcompress('wrong-header'));
$handle = bzopen('report.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    try {
        if (bzread($handle) !== 'id;qty') {
            throw new RuntimeException('Неверный заголовок');
        }
    } finally { bzclose($handle); }
} catch (RuntimeException $error) {
    echo $error->getMessage(), "\n";
}
var_dump(is_resource($handle));
Ожидаемый результат
Неверный заголовок
bool(false)

Внутренний finally освобождает ресурс, внешний catch сообщает причину отказа. Прикладная ошибка не исчезает из-за закрытия файла.

Границы применения. Состояние bzerror считывайте до закрытия. При передаче в bzopen существующего файлового ресурса закрытие обёртки закрывает также исходный ресурс в проверенной реализации. Закрывать дважды не нужно.

Связанные действия: bzopen(), bzerror(), bzflush().

bzflushРаспознать ограничение старого совместимого API: этот вызов не делает полезной принудительной выгрузки Bzip2-буфера.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzflush(resource $bz): bool

Распознать ограничение старого совместимого API: этот вызов не делает полезной принудительной выгрузки Bzip2-буфера.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzflush(resource $bz): bool

ПараметрДоговор
$bzОткрытый пишущий ресурс Bzip2.

Результат и ошибки. Булев результат вызова. Официальная документация прямо отмечает: библиотечный BZ2_bzflush ничего не делает. true нельзя трактовать как готовность файла к чтению, завершение потока или гарантию сохранения на диск.

Данные в первом примере
  1. ABC записывается в Bzip2-поток
  2. bzflush может вернуть true, но не завершает поток
  3. Только затем bzclose; читаем готовый файл
Почему true не заменяет закрытиеСамостоятельный пример
<?php
$handle = bzopen('flush.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
bzwrite($handle, 'ABC');
var_dump(bzflush($handle));
bzclose($handle);
echo bzdecompress(file_get_contents('flush.bz2')), "\n";
Ожидаемый результат
bool(true)
ABC

Чтение сделано после bzclose. Этот опыт намеренно не приписывает bzflush появление готового результата.

Старый код с flush продолжает тот же потокСамостоятельный пример
<?php
$handle = bzopen('parts.bz2', 'w');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzwrite($handle, "part-1\n");
    bzflush($handle);
    bzwrite($handle, "part-2\n");
} finally { bzclose($handle); }
echo bzdecompress(file_get_contents('parts.bz2'));
Ожидаемый результат
part-1
part-2

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

Границы применения. Не переносите обещания fflush обычного потока на эту обёртку. Если читатель должен видеть обновления в реальном времени, выберите иной протокол/несжатый журнал либо закрывайте отдельные сжатые части.

Связанные действия: bzclose(), bzwrite().

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

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzerrno(resource $bz): int

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

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzerrno(resource $bz): int

ПараметрДоговор
$bzЕщё открытый ресурс именно Bzip2 после проверяемой операции. Закрытый или обычный fopen-ресурс не подходит для этих диагностических функций.

Результат и ошибки. Целое число: 0 означает отсутствие ошибки, отрицательное — отказ. Это не число прочитанных байтов и не HTTP-код. Если bzopen вернул false, диагностического ресурса ещё нет: эту функцию не вызывают.

Данные в первом примере
  1. Файл содержит корректный сжатый OK
  2. bzread читает его
  3. Диагностика сообщает отсутствие ошибки
Проверить состояние корректного чтенияСамостоятельный пример
<?php
file_put_contents('ok.bz2', bzcompress('OK'));
$handle = bzopen('ok.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    echo bzerrno($handle), "\n";
} finally { bzclose($handle); }
Ожидаемый результат
0

Проверка относится к операции чтения на этом ресурсе. Для целого кода из bzdecompress нет отдельного ресурса, который можно передать сюда.

Теперь имя заканчивается .bz2, но содержимое обычное. bzopen смог открыть файл; ошибка формата появилась при bzread. Результат чтения не используют до проверки состояния.

Отличить имя файла от настоящего форматаСамостоятельный пример
<?php
file_put_contents('fake.bz2', 'ordinary report');
$handle = bzopen('fake.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    echo bzerrno($handle) === -5 ? "Неверный формат\n" : "Другая ошибка\n";
} finally { bzclose($handle); }
Ожидаемый результат
Неверный формат

Показанное обозначение пришло из libbzip2 1.0.8. В пользовательском интерфейсе лучше вывести «Не удалось прочитать отчёт», а техническое состояние записать во внутреннюю диагностику.

Границы применения. Считайте состояние сразу после операции и до bzclose. В PHP эти имена библиотечных ошибок не экспортированы как BZ_* константы. Таблица кодов есть в разделе диагностики. Код 0 после конца чтения не доказывает отсутствие лишних байтов/второго сжатого потока после первого.

Связанные действия: bzerrstr(), bzerror(), bzread().

bzerrstrПолучить техническое описание ошибки для внутренней диагностики.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzerrstr(resource $bz): string

Получить техническое описание ошибки для внутренней диагностики.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzerrstr(resource $bz): string

ПараметрДоговор
$bzЕщё открытый ресурс именно Bzip2 после проверяемой операции. Закрытый или обычный fopen-ресурс не подходит для этих диагностических функций.

Результат и ошибки. Строка технического состояния, например OK или DATA_ERROR_MAGIC. Она не предназначена для прямого сообщения пользователю и не заменяет проверку кода. Если bzopen вернул false, диагностического ресурса ещё нет: эту функцию не вызывают.

Данные в первом примере
  1. Файл содержит корректный сжатый OK
  2. bzread читает его
  3. Диагностика сообщает отсутствие ошибки
Проверить состояние корректного чтенияСамостоятельный пример
<?php
file_put_contents('ok.bz2', bzcompress('OK'));
$handle = bzopen('ok.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    echo bzerrstr($handle), "\n";
} finally { bzclose($handle); }
Ожидаемый результат
OK

Проверка относится к операции чтения на этом ресурсе. Для целого кода из bzdecompress нет отдельного ресурса, который можно передать сюда.

Теперь имя заканчивается .bz2, но содержимое обычное. bzopen смог открыть файл; ошибка формата появилась при bzread. Результат чтения не используют до проверки состояния.

Отличить имя файла от настоящего форматаСамостоятельный пример
<?php
file_put_contents('fake.bz2', 'ordinary report');
$handle = bzopen('fake.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    echo bzerrstr($handle), "\n";
} finally { bzclose($handle); }
Ожидаемый результат
DATA_ERROR_MAGIC

Показанное обозначение пришло из libbzip2 1.0.8. В пользовательском интерфейсе лучше вывести «Не удалось прочитать отчёт», а техническое состояние записать во внутреннюю диагностику.

Границы применения. Считайте состояние сразу после операции и до bzclose. В PHP эти имена библиотечных ошибок не экспортированы как BZ_* константы. Таблица кодов есть в разделе диагностики. Код 0 после конца чтения не доказывает отсутствие лишних байтов/второго сжатого потока после первого.

Связанные действия: bzerrno(), bzerror(), bzread().

bzerrorПолучить код и описание одной ошибочной операции вместе.

PHP 4.0.4+; примеры PHP 8.2+ · bz2 / libbzip2

bzerror(resource $bz): array

Получить код и описание одной ошибочной операции вместе.

Вызов имя(...) передаёт значения функции; запятые разделяют аргументы. $name = ...; сохраняет результат, echo выводит его, а var_dump показывает также тип. "\n" — перевод строки. === false проверяет именно отказ, не пустой текст и не строку "0". Переменная; Вызов функции.

bzopen возвращает ресурс — временный указатель на открытый поток, либо false при отказе. Это не содержимое файла. if (...) { ... } выбирает ветку; throw new RuntimeException(...) останавливает обычный путь с описанием ошибки. В try { ... } finally { bzclose($handle); } блок finally освобождает поток даже при исключении, но не подавляет ошибку. Потоки и позиция чтения; Обработка ошибки.

bzerror(resource $bz): array

ПараметрДоговор
$bzЕщё открытый ресурс именно Bzip2 после проверяемой операции. Закрытый или обычный fopen-ресурс не подходит для этих диагностических функций.

Результат и ошибки. Ассоциативный массив с errno (int) и errstr (string). Ключи читают через квадратные скобки: $error['errno']. Массив сам по себе не булев признак ошибки. Если bzopen вернул false, диагностического ресурса ещё нет: эту функцию не вызывают.

Данные в первом примере
  1. Файл содержит корректный сжатый OK
  2. bzread читает его
  3. Диагностика сообщает отсутствие ошибки
Проверить состояние корректного чтенияСамостоятельный пример
<?php
file_put_contents('ok.bz2', bzcompress('OK'));
$handle = bzopen('ok.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    $error = bzerror($handle);
echo $error['errno'], ":", $error['errstr'], "\n";
} finally { bzclose($handle); }
Ожидаемый результат
0:OK

Проверка относится к операции чтения на этом ресурсе. Для целого кода из bzdecompress нет отдельного ресурса, который можно передать сюда.

Теперь имя заканчивается .bz2, но содержимое обычное. bzopen смог открыть файл; ошибка формата появилась при bzread. Результат чтения не используют до проверки состояния.

Отличить имя файла от настоящего форматаСамостоятельный пример
<?php
file_put_contents('fake.bz2', 'ordinary report');
$handle = bzopen('fake.bz2', 'r');
if ($handle === false) { throw new RuntimeException('Не открыт файл'); }
try {
    bzread($handle);
    $error = bzerror($handle);
echo $error['errno'], ":", $error['errstr'], "\n";
} finally { bzclose($handle); }
Ожидаемый результат
-5:DATA_ERROR_MAGIC

Показанное обозначение пришло из libbzip2 1.0.8. В пользовательском интерфейсе лучше вывести «Не удалось прочитать отчёт», а техническое состояние записать во внутреннюю диагностику.

Границы применения. Считайте состояние сразу после операции и до bzclose. В PHP эти имена библиотечных ошибок не экспортированы как BZ_* константы. Таблица кодов есть в разделе диагностики. Код 0 после конца чтения не доказывает отсутствие лишних байтов/второго сжатого потока после первого.

Связанные действия: bzerrno(), bzerrstr(), bzread().

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

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

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

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

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