はじめに
前回の記事では、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)と実質同等の限定版 |
よくある落とし穴(注意点)
- 用途に合わない圧縮形式を選んでしまう
.gzファイルとして配布したいのにZLIB_ENCODING_RAWを使ってしまうと、標準的なgzipツールで展開できないファイルになります。相手システムやツールが期待する形式を事前に確認しましょう(例1・例6を参照)。 - 圧縮レベルを最大にすれば常に良いと誤解する 圧縮レベルを9(最大)にすると圧縮率は上がりますが、処理時間も増加します。リアルタイム性が求められる処理では、レベル6程度のバランスの取れた値や、より低いレベルを検討する価値があります(例3を参照)。
- 既に圧縮済みのデータ(画像やzipファイルなど)を再圧縮しようとする JPEGやPNG、既にzip圧縮されたファイルなどは、それ自体が高いエントロピーを持つため、
zlib_encode()でさらに圧縮してもほとんどサイズが縮まらず、逆にわずかに増加することさえあります。 - 小さなデータに対して圧縮のオーバーヘッドを考慮しない 非常に短い文字列を圧縮すると、圧縮ヘッダーなどのオーバーヘッドにより、圧縮後の方がかえってサイズが大きくなることがあります。圧縮を適用する最小サイズの閾値を設けるのも一つの工夫です。
- 失敗時の
falseチェックを省略する メモリ不足など何らかの理由で圧縮に失敗した場合、戻り値はfalseになります。この確認を怠ると、後続の処理で不正なデータを扱ってしまう可能性があります。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | 指定した形式(raw/deflate/gzip)でデータを圧縮する |
| 主な用途 | HTTPレスポンスの圧縮、データベース保存前のサイズ削減、gzipファイルの生成 |
| 3つの形式の使い分け | RAW(最小オーバーヘッド)、DEFLATE(zlib互換)、GZIP(.gzファイル互換) |
| 対になる関数 | zlib_decode()(解凍) |
| 注意点 | 用途に合った形式の選択、圧縮レベルとパフォーマンスのトレードオフ、既圧縮データへの再圧縮の無意味さ |
zlib_encode() は、単一の関数で複数の圧縮形式に対応できる柔軟性の高い関数です。用途に応じた適切な形式選択と圧縮レベルの調整を行うことで、データサイズの削減とパフォーマンスのバランスを取った効率的な実装が可能になります。
