はじめに
Webアプリケーションでは、通信量を減らすためにデータをgzip形式やzlib形式で圧縮して扱う場面がよくあります。外部APIから圧縮されたレスポンスを受け取ったり、圧縮済みのデータをファイルやデータベースに保存していたりする場合、それを元の状態に戻す(解凍する)処理が必要になります。
zlib_decode() は、そうした圧縮データを解凍するための関数です。この関数の便利な点は、gzip形式・zlib形式・raw deflate形式のいずれであっても、ヘッダー情報を自動的に判別して適切に解凍してくれるという点にあります。gzuncompress() や gzinflate() のように圧縮形式を限定する関数と異なり、zlib_decode() は汎用的な解凍関数として位置づけられています。本記事では基本的な使い方から実践的な活用例まで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | zlib_decode() |
| 所属拡張 | zlib拡張(多くの環境で標準的に有効) |
| シグネチャ | zlib_decode(string $data, int $max_length = 0): string|false |
| 引数1 | $data — 解凍対象の圧縮データ |
| 引数2 | $max_length — 解凍後データの最大長(0で無制限) |
| 戻り値 | 解凍された文字列。失敗時は false |
| 対応バージョン | PHP 5.4以降 |
| 対応形式 | zlib、gzip、raw deflateを自動判別 |
| 対になる関数 | zlib_encode()(圧縮) |
自動判別の仕組み(イメージ図)
圧縮データ(形式不明)
│
▼
zlib_decode($data)
│
┌──────────┴──────────────────┐
│ データ先頭のマジックバイト(ヘッダー) │
│ を確認し、圧縮形式を自動判別する │
└──────────┬──────────────────┘
▼
┌──────────┬──────────┬──────────┐
│ ZLIB形式 │ GZIP形式 │ RAW DEFLATE │
│ (0x78で開始) │ (0x1F 0x8Bで開始)│ (ヘッダーなし)│
└──────────┴──────────┴──────────┘
│
▼
元の展開されたデータ
ポイントは、gzuncompress()(zlib形式専用)、gzdecode()(gzip形式専用)、gzinflate()(raw deflate専用)と個別に使い分ける必要がある他の関数群と違い、zlib_decode() はデータの形式を意識せずに1つの関数で対応できるという利便性です。
実践サンプル7選
例1:基本的な使い方
<?php
class BasicZlibDecoder
{
public function decode(string $compressed): string|false
{
// 圧縮形式を自動判別して解凍する
return zlib_decode($compressed);
}
}
$decoder = new BasicZlibDecoder();
$original = 'これはテストデータです。' . str_repeat('繰り返し', 10);
$compressed = zlib_encode($original, ZLIB_ENCODING_GZIP);
echo $decoder->decode($compressed) . PHP_EOL;
例2:外部APIから受け取った圧縮レスポンスを解凍するクラス
<?php
class CompressedApiResponseHandler
{
/**
* Content-Encodingヘッダーに応じて、
* 圧縮されたレスポンスボディを解凍する
*/
public function handleResponse(string $body, string $contentEncoding): string
{
if (in_array($contentEncoding, ['gzip', 'deflate'], true)) {
$decoded = zlib_decode($body);
if ($decoded === false) {
throw new RuntimeException('レスポンスの解凍に失敗しました');
}
return $decoded;
}
return $body;
}
}
$handler = new CompressedApiResponseHandler();
$compressed = zlib_encode('{"status":"ok"}', ZLIB_ENCODING_DEFLATE);
echo $handler->handleResponse($compressed, 'deflate') . PHP_EOL;
例3:解凍後のサイズ上限を指定して安全に処理するクラス
<?php
class SafeDecompressor
{
/**
* 第2引数で解凍後の最大サイズを指定し、
* 巨大な展開(zip爆弾のような攻撃)からメモリを保護する
*/
public function decodeWithLimit(string $compressed, int $maxBytes): string
{
$result = zlib_decode($compressed, $maxBytes);
if ($result === false) {
throw new RuntimeException('解凍に失敗しました(サイズ上限超過の可能性があります)');
}
return $result;
}
}
$decompressor = new SafeDecompressor();
$compressed = zlib_encode(str_repeat('a', 1000), ZLIB_ENCODING_GZIP);
echo strlen($decompressor->decodeWithLimit($compressed, 2000)) . PHP_EOL;
例4:データベースに保存された圧縮データを読み出すクラス
<?php
class CompressedBlobReader
{
public function __construct(private PDO $pdo)
{
}
/**
* ストレージ節約のため圧縮して保存していたBLOBデータを
* 取得時に自動解凍する
*/
public function readContent(int $id): ?string
{
$stmt = $this->pdo->prepare('SELECT compressed_content FROM documents WHERE id = :id');
$stmt->execute(['id' => $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
return null;
}
$result = zlib_decode($row['compressed_content']);
return $result !== false ? $result : null;
}
}
例5:解凍と圧縮の往復(ラウンドトリップ)を検証するテストツール
<?php
class RoundTripVerifier
{
/**
* zlib_encode()で圧縮したデータをzlib_decode()で解凍し、
* 元のデータと一致するかを検証する
*/
public function verify(string $original, int $encoding = ZLIB_ENCODING_GZIP): bool
{
$compressed = zlib_encode($original, $encoding);
$decompressed = zlib_decode($compressed);
return $decompressed === $original;
}
}
$verifier = new RoundTripVerifier();
var_dump($verifier->verify('検証用のサンプルテキストです。'));
var_dump($verifier->verify('別の検証用テキスト', ZLIB_ENCODING_RAW));
例6:複数の圧縮形式が混在するデータを一括処理するツール
<?php
class MixedFormatBatchDecoder
{
/**
* gzip、zlib、raw deflateが混在する複数のデータを、
* zlib_decode()の自動判別機能を活かして一括処理する
*/
public function decodeAll(array $compressedItems): array
{
$results = [];
foreach ($compressedItems as $key => $data) {
$decoded = zlib_decode($data);
$results[$key] = $decoded !== false ? $decoded : null;
}
return $results;
}
}
$decoder = new MixedFormatBatchDecoder();
$items = [
'gzip_item' => zlib_encode('gzipデータ', ZLIB_ENCODING_GZIP),
'zlib_item' => zlib_encode('zlibデータ', ZLIB_ENCODING_DEFLATE),
'raw_item' => zlib_encode('rawデータ', ZLIB_ENCODING_RAW),
];
print_r($decoder->decodeAll($items));
例7:解凍失敗時のフォールバック処理を含む堅牢な実装
<?php
class FallbackDecoder
{
/**
* 解凍に失敗した場合、元のデータが
* 圧縮されていなかった可能性を考慮してそのまま返す
*/
public function decodeOrPassthrough(string $data): string
{
$result = @zlib_decode($data);
// 解凍に失敗した場合、圧縮されていない生データの可能性がある
return $result !== false ? $result : $data;
}
}
$decoder = new FallbackDecoder();
$compressed = zlib_encode('圧縮されたデータ', ZLIB_ENCODING_GZIP);
$plain = '圧縮されていない普通の文字列';
echo $decoder->decodeOrPassthrough($compressed) . PHP_EOL;
echo $decoder->decodeOrPassthrough($plain) . PHP_EOL;
関連関数との比較
| 関数 | 役割 | zlib_decodeとの違い |
|---|---|---|
zlib_decode() | 圧縮形式を自動判別して解凍 | 本記事の対象。gzip/zlib/raw deflateすべてに対応 |
zlib_encode() | データを指定形式で圧縮 | zlib_decode()の対になるエンコード関数 |
gzuncompress() | zlib形式専用の解凍 | 形式がzlibであることが既知の場合に使う、より限定的な関数 |
gzdecode() | gzip形式専用の解凍 | 形式がgzipであることが既知の場合に使う、より限定的な関数 |
gzinflate() | raw deflate形式専用の解凍 | ヘッダーなしのraw deflate形式専用(形式が既知の場合向け) |
よくある落とし穴(注意点)
- 解凍後のデータサイズに上限を設けないと危険な場合がある 信頼できない外部ソースから受け取った圧縮データをそのまま解凍すると、非常に小さい圧縮データが巨大な展開後データになる「zip爆弾」のような攻撃によって、メモリを枯渇させられるリスクがあります。第2引数の
$max_lengthを活用し、想定される最大サイズを指定しておくことが重要です(例3を参照)。 - 失敗時の
falseと、値としての空文字列を混同しない 解凍に失敗するとfalseが返りますが、厳密な比較(=== false)を行わないと、空文字列などの正当な結果と区別できない場合があります。 - 圧縮されていないデータをそのまま渡すとエラーになる
zlib_decode()に非圧縮のプレーンなデータを渡すと解凍に失敗します。データが圧縮されているかどうかが不明な場合は、事前の判定やフォールバック処理を組み込むと安全です(例7を参照)。 - 形式限定の関数(
gzuncompress()など)との使い分けを意識する 相手システムの圧縮形式が明確に分かっている場合、あえて自動判別を行うzlib_decode()を使わず、形式を限定した専用関数を使う方が、意図しない形式のデータを誤って処理してしまうリスクを避けられる場合があります。 - 拡張が無効化されている環境も稀にある 多くの環境で標準的に有効なzlib拡張ですが、最小構成でビルドされたPHP環境などでは無効化されている可能性もゼロではありません。念のため
function_exists('zlib_decode')で確認しておくと安全です。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | gzip・zlib・raw deflate形式の圧縮データを自動判別して解凍する |
| 主な用途 | 外部APIからの圧縮レスポンスの解凍、圧縮保存されたデータの読み出し |
| 最大の特徴 | 圧縮形式を意識せず、1つの関数で複数形式に対応できる |
| 対になる関数 | zlib_encode()(圧縮) |
| 注意点 | 解凍後サイズの上限設定(zip爆弾対策)、失敗時のfalse判定、非圧縮データへの対処 |
zlib_decode() は、圧縮形式を意識せずにデータを解凍できる、利便性の高い関数です。ただし、信頼できない外部データを扱う際は、解凍後のサイズ上限を必ず設定するなど、リソース枯渇攻撃への配慮を忘れずに実装することが重要です。
