[PHP]zlib_encodeとは?データをgzip/zlib形式で圧縮する方法を徹底解説

PHP

はじめに

前回の記事では、gzip・zlib・raw deflate形式の圧縮データを自動判別して解凍する zlib_decode() を解説しました。今回はその対となる、データを圧縮するための関数 zlib_encode() を取り上げます。

APIレスポンスのペイロードを軽量化したい、大きなテキストデータをファイルやデータベースに保存する前に圧縮しておきたい、といった場面で活躍するのが zlib_encode() です。この関数の特徴は、gzcompress()gzencode()gzdeflate() という3つの個別の圧縮関数が持つ機能を、第2引数の指定によって1つの関数で使い分けられるという点にあります。本記事では基本的な使い方から、実践的な活用パターンまで詳しく解説します。


関数概要

項目内容
関数名zlib_encode()
所属拡張zlib拡張(多くの環境で標準的に有効)
シグネチャzlib_encode(string $data, int $encoding, int $level = -1): string|false
引数1$data — 圧縮対象の文字列
引数2$encoding — 圧縮形式(ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP, ZLIB_ENCODING_DEFLATE
引数3$level — 圧縮レベル(-1〜9、-1はデフォルト値を使用)
戻り値圧縮された文字列。失敗時は false
対応バージョンPHP 5.4以降
対になる関数zlib_decode()(解凍)

3つの圧縮形式(イメージ図)

  元のデータ
  "圧縮したいテキストデータです..."
              │
              ▼
  zlib_encode($data, $encoding, $level)
              │
   ┌──────────┼──────────────┬──────────────┐
   ▼          ▼              ▼
  ZLIB_ENCODING_  ZLIB_ENCODING_  ZLIB_ENCODING_
  RAW             DEFLATE         GZIP
   │              │               │
   ▼              ▼               ▼
  ヘッダーなしの   zlibヘッダー付き   gzipヘッダー付き
  raw deflate     (gzcompress相当)  (gzencode相当、
  (gzdeflate相当)                   .gzファイル互換)

ポイントは、用途に応じて適切な形式を選ぶ必要があるという点です。.gz ファイルとしての互換性が必要なら ZLIB_ENCODING_GZIP、HTTPの Content-Encoding: deflate との互換性が必要なら ZLIB_ENCODING_DEFLATE、単純にアプリケーション内部だけでデータを圧縮・展開したいなら、ヘッダー分のオーバーヘッドが最も少ない ZLIB_ENCODING_RAW が適しています。


実践サンプル7選

例1:基本的な使い方(3つの形式の比較)

<?php

class BasicEncodingComparator
{
    public function compareFormats(string $data): array
    {
        return [
            'raw'     => zlib_encode($data, ZLIB_ENCODING_RAW),
            'deflate' => zlib_encode($data, ZLIB_ENCODING_DEFLATE),
            'gzip'    => zlib_encode($data, ZLIB_ENCODING_GZIP),
        ];
    }
}

$comparator = new BasicEncodingComparator();
$results = $comparator->compareFormats(str_repeat('サンプルテキスト', 20));
foreach ($results as $format => $compressed) {
    echo "{$format}: " . strlen($compressed) . " bytes" . PHP_EOL;
}

例2:APIレスポンスをgzip圧縮して送信するクラス

<?php

class GzipResponseCompressor
{
    /**
     * HTTPレスポンスをgzip形式で圧縮し、
     * 対応する適切なヘッダーを設定する
     */
    public function compressResponse(string $body): array
    {
        $compressed = zlib_encode($body, ZLIB_ENCODING_GZIP);

        return [
            'body'    => $compressed,
            'headers' => [
                'Content-Encoding' => 'gzip',
                'Content-Length'   => (string) strlen($compressed),
            ],
        ];
    }
}

$compressor = new GzipResponseCompressor();
$result = $compressor->compressResponse(json_encode(['data' => str_repeat('x', 1000)]));
print_r($result['headers']);

例3:圧縮レベルを指定してサイズと速度のバランスを調整するクラス

<?php

class TunableCompressor
{
    /**
     * 圧縮レベル(0〜9)を指定することで、
     * 圧縮速度と圧縮率のトレードオフを調整する
     */
    public function compressWithLevel(string $data, int $level): string
    {
        // レベル0は無圧縮(高速)、9は最大圧縮(低速)
        return zlib_encode($data, ZLIB_ENCODING_GZIP, $level);
    }

    public function benchmarkLevels(string $data): array
    {
        $results = [];
        foreach ([1, 5, 9] as $level) {
            $start = microtime(true);
            $compressed = $this->compressWithLevel($data, $level);
            $results[$level] = [
                'size_bytes' => strlen($compressed),
                'time_ms'    => round((microtime(true) - $start) * 1000, 3),
            ];
        }
        return $results;
    }
}

$tuner = new TunableCompressor();
print_r($tuner->benchmarkLevels(str_repeat('テストデータ', 500)));

例4:大きなテキストをデータベースに保存する前に圧縮するクラス

<?php

class CompressedContentStorage
{
    public function __construct(private PDO $pdo)
    {
    }

    /**
     * ストレージ容量を節約するため、
     * 保存前にコンテンツをzlib形式で圧縮する
     */
    public function store(string $documentId, string $content): void
    {
        $compressed = zlib_encode($content, ZLIB_ENCODING_DEFLATE, 6);

        $stmt = $this->pdo->prepare(
            'INSERT INTO documents (id, compressed_content, original_size, compressed_size)
             VALUES (:id, :content, :original_size, :compressed_size)'
        );
        $stmt->execute([
            'id'               => $documentId,
            'content'          => $compressed,
            'original_size'    => strlen($content),
            'compressed_size'  => strlen($compressed),
        ]);
    }
}

例5:圧縮率を計算して効果を確認するユーティリティ

<?php

class CompressionRatioCalculator
{
    /**
     * 圧縮前後のサイズを比較し、
     * 削減率をパーセンテージで算出する
     */
    public function calculate(string $original): array
    {
        $compressed = zlib_encode($original, ZLIB_ENCODING_GZIP, 9);
        $originalSize = strlen($original);
        $compressedSize = strlen($compressed);
        $ratio = $originalSize > 0 ? round((1 - $compressedSize / $originalSize) * 100, 1) : 0;

        return [
            'original_bytes'   => $originalSize,
            'compressed_bytes' => $compressedSize,
            'reduction_percent' => $ratio,
        ];
    }
}

$calculator = new CompressionRatioCalculator();
print_r($calculator->calculate(str_repeat('繰り返しの多いテキストデータ', 100)));

例6:圧縮したデータをファイルとして書き出すクラス

<?php

class GzipFileWriter
{
    /**
     * .gz拡張子のファイルとして書き出すことで、
     * 標準的なgzipツールでも展開できる形式にする
     */
    public function writeGzipFile(string $content, string $filePath): bool
    {
        $compressed = zlib_encode($content, ZLIB_ENCODING_GZIP);
        $result = file_put_contents($filePath, $compressed);

        return $result !== false;
    }
}

$writer = new GzipFileWriter();
var_dump($writer->writeGzipFile('ログファイルの内容です...', '/tmp/output.log.gz'));

例7:圧縮・解凍のラウンドトリップを含むキャッシュシステム

<?php

class CompressedCacheStore
{
    /**
     * キャッシュデータを圧縮して保存し、
     * 取得時に自動解凍する簡易的なキャッシュシステム
     */
    private array $store = [];

    public function set(string $key, string $value): void
    {
        $this->store[$key] = zlib_encode($value, ZLIB_ENCODING_RAW, 3);
    }

    public function get(string $key): ?string
    {
        if (!isset($this->store[$key])) {
            return null;
        }

        $result = zlib_decode($this->store[$key]);
        return $result !== false ? $result : null;
    }
}

$cache = new CompressedCacheStore();
$cache->set('greeting', str_repeat('こんにちは、世界!', 50));
echo substr($cache->get('greeting'), 0, 20) . '...' . PHP_EOL;

関連関数との比較

関数役割zlib_encodeとの違い
zlib_encode()指定形式でデータを圧縮本記事の対象。第2引数で3つの形式を選べる
zlib_decode()圧縮形式を自動判別して解凍zlib_encode()の対になるデコード関数
gzencode()gzip形式専用の圧縮zlib_encode($data, ZLIB_ENCODING_GZIP)と実質同等の限定版
gzcompress()zlib形式専用の圧縮zlib_encode($data, ZLIB_ENCODING_DEFLATE)と実質同等の限定版
gzdeflate()raw deflate形式専用の圧縮zlib_encode($data, ZLIB_ENCODING_RAW)と実質同等の限定版

よくある落とし穴(注意点)

  1. 用途に合わない圧縮形式を選んでしまう .gz ファイルとして配布したいのに ZLIB_ENCODING_RAW を使ってしまうと、標準的なgzipツールで展開できないファイルになります。相手システムやツールが期待する形式を事前に確認しましょう(例1・例6を参照)。
  2. 圧縮レベルを最大にすれば常に良いと誤解する 圧縮レベルを9(最大)にすると圧縮率は上がりますが、処理時間も増加します。リアルタイム性が求められる処理では、レベル6程度のバランスの取れた値や、より低いレベルを検討する価値があります(例3を参照)。
  3. 既に圧縮済みのデータ(画像やzipファイルなど)を再圧縮しようとする JPEGやPNG、既にzip圧縮されたファイルなどは、それ自体が高いエントロピーを持つため、zlib_encode() でさらに圧縮してもほとんどサイズが縮まらず、逆にわずかに増加することさえあります。
  4. 小さなデータに対して圧縮のオーバーヘッドを考慮しない 非常に短い文字列を圧縮すると、圧縮ヘッダーなどのオーバーヘッドにより、圧縮後の方がかえってサイズが大きくなることがあります。圧縮を適用する最小サイズの閾値を設けるのも一つの工夫です。
  5. 失敗時のfalseチェックを省略する メモリ不足など何らかの理由で圧縮に失敗した場合、戻り値は false になります。この確認を怠ると、後続の処理で不正なデータを扱ってしまう可能性があります。

まとめ

観点まとめ
何をする関数か指定した形式(raw/deflate/gzip)でデータを圧縮する
主な用途HTTPレスポンスの圧縮、データベース保存前のサイズ削減、gzipファイルの生成
3つの形式の使い分けRAW(最小オーバーヘッド)、DEFLATE(zlib互換)、GZIP(.gzファイル互換)
対になる関数zlib_decode()(解凍)
注意点用途に合った形式の選択、圧縮レベルとパフォーマンスのトレードオフ、既圧縮データへの再圧縮の無意味さ

zlib_encode() は、単一の関数で複数の圧縮形式に対応できる柔軟性の高い関数です。用途に応じた適切な形式選択と圧縮レベルの調整を行うことで、データサイズの削減とパフォーマンスのバランスを取った効率的な実装が可能になります。

タイトルとURLをコピーしました