[PHP]zip_entry_compressedsizeとは?非推奨のZIPエントリ圧縮後サイズ取得関数とZipArchiveへの移行を徹底解説

PHP

はじめに

これまでの記事で、非推奨の手続き型ZIP関数群から zip_close()zip_entry_close() を解説してきました。今回取り上げる zip_entry_compressedsize() も同じ非推奨の関数群に属し、ZIPアーカイブ内の各エントリが圧縮された状態でどれだけのサイズを占めているかを取得するための関数です。

ZIPファイルのエントリには「圧縮前のサイズ(元のファイルサイズ)」と「圧縮後のサイズ(アーカイブ内で実際に占めるサイズ)」という2つの異なるサイズ情報があります。zip_entry_compressedsize() は後者、つまり圧縮後のサイズを取得するための関数です。前回までの記事と同様、この関数もPHP 7.2.0以降で非推奨とされており、現在は ZipArchive クラスの利用が推奨されています。本記事では、この関数の役割と、ZipArchive を使った現代的な代替実装を詳しく解説します。


関数概要

項目内容
関数名zip_entry_compressedsize()
所属拡張Zip拡張(手続き型インターフェース)
シグネチャzip_entry_compressedsize(resource $zip_entry): int
引数$zip_entryzip_read() などで取得したエントリのリソース
戻り値エントリの圧縮後のバイト数
対応バージョンPHP 4.1.0以降。PHP 7.2.0以降は非推奨
対になる情報zip_entry_filesize()(圧縮前のサイズ)
推奨される代替ZipArchive::statIndex() / statName()'comp_size' フィールド

2種類のサイズ情報(イメージ図)

  ZIPアーカイブ内のエントリ "document.txt"
              │
   ┌──────────┴──────────────────┐
   ▼                                ▼
  圧縮前のサイズ                    圧縮後のサイズ
  (元のファイルサイズ)               (アーカイブ内で実際に占める容量)
  zip_entry_filesize()             zip_entry_compressedsize()
  例: 10,000 バイト                 ★この記事の対象
                                    例: 3,200 バイト
                                    (圧縮率68%相当)

ポイントは、この2つのサイズの差分こそが圧縮によって削減された容量を表すという点です。この情報を使うことで、圧縮効率の確認や、展開後に必要なディスク容量の見積もりなど、実用的な判断材料が得られます。


実践サンプル7選

例1:非推奨の古い関数を使った基本的な使い方(参考・非推奨)

<?php

class LegacyCompressedSizeReader
{
    /**
     * 注意: PHP 7.2.0以降、この一連の関数は非推奨です
     * 新規開発ではZipArchiveの使用を強く推奨します
     */
    public function getSizes(string $zipPath): array
    {
        $sizes = [];
        $zip = zip_open($zipPath);

        if (is_resource($zip)) {
            while ($entry = zip_read($zip)) {
                $name = zip_entry_name($entry);
                $sizes[$name] = [
                    'original'   => zip_entry_filesize($entry),
                    'compressed' => zip_entry_compressedsize($entry),
                ];
            }
            zip_close($zip);
        }

        return $sizes;
    }
}

$reader = new LegacyCompressedSizeReader();
print_r($reader->getSizes('/tmp/sample.zip'));

例2:ZipArchiveを使った推奨の代替実装

<?php

class ModernCompressedSizeReader
{
    /**
     * ZipArchive::statIndex()が返す配列の
     * 'comp_size'キーで圧縮後サイズを取得できる
     */
    public function getSizes(string $zipPath): array
    {
        $sizes = [];
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $sizes[$stat['name']] = [
                    'original'   => $stat['size'],
                    'compressed' => $stat['comp_size'],
                ];
            }
            $zip->close();
        }

        return $sizes;
    }
}

$reader = new ModernCompressedSizeReader();
print_r($reader->getSizes('/tmp/sample.zip'));

例3:圧縮率を計算して一覧表示するクラス

<?php

class CompressionRatioReporter
{
    /**
     * 圧縮前後のサイズから圧縮率を算出し、
     * どのエントリが効率よく圧縮されているかを確認する
     */
    public function report(string $zipPath): array
    {
        $report = [];
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $ratio = $stat['size'] > 0
                    ? round((1 - $stat['comp_size'] / $stat['size']) * 100, 1)
                    : 0;

                $report[] = [
                    'name'  => $stat['name'],
                    'ratio' => "{$ratio}%",
                ];
            }
            $zip->close();
        }

        return $report;
    }
}

$reporter = new CompressionRatioReporter();
print_r($reporter->report('/tmp/sample.zip'));

例4:展開に必要なディスク容量を事前に見積もるクラス

<?php

class ExtractionSpaceEstimator
{
    /**
     * アーカイブ内の全エントリの圧縮前サイズを合計し、
     * 展開に必要なディスク容量を事前に見積もる
     */
    public function estimateRequiredSpace(string $zipPath): int
    {
        $totalSize = 0;
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $totalSize += $stat['size'];
            }
            $zip->close();
        }

        return $totalSize;
    }

    public function hasEnoughDiskSpace(string $zipPath, string $targetDir): bool
    {
        $required = $this->estimateRequiredSpace($zipPath);
        $available = disk_free_space($targetDir);

        return $available !== false && $available > $required;
    }
}

$estimator = new ExtractionSpaceEstimator();
var_dump($estimator->hasEnoughDiskSpace('/tmp/sample.zip', '/tmp'));

例5:圧縮率が悪いエントリ(既に圧縮済みのファイルなど)を検出するツール

<?php

class LowCompressionDetector
{
    /**
     * 圧縮率が極端に低いエントリ(既にJPEG/PNG/ZIP等で
     * 圧縮済みの可能性があるファイル)を検出する
     */
    public function detectLowCompression(string $zipPath, float $thresholdPercent = 5.0): array
    {
        $detected = [];
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                if ($stat['size'] === 0) {
                    continue;
                }

                $ratio = (1 - $stat['comp_size'] / $stat['size']) * 100;
                if ($ratio < $thresholdPercent) {
                    $detected[] = $stat['name'];
                }
            }
            $zip->close();
        }

        return $detected;
    }
}

$detector = new LowCompressionDetector();
print_r($detector->detectLowCompression('/tmp/sample.zip'));

例6:アーカイブ全体の圧縮効率をサマリーとして出力するツール

<?php

class ArchiveCompressionSummary
{
    /**
     * アーカイブ全体での合計圧縮前サイズ・合計圧縮後サイズ・
     * 総合的な圧縮率をまとめて算出する
     */
    public function summarize(string $zipPath): array
    {
        $totalOriginal = 0;
        $totalCompressed = 0;
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $stat = $zip->statIndex($i);
                $totalOriginal += $stat['size'];
                $totalCompressed += $stat['comp_size'];
            }
            $zip->close();
        }

        $overallRatio = $totalOriginal > 0
            ? round((1 - $totalCompressed / $totalOriginal) * 100, 1)
            : 0;

        return [
            'total_original_bytes'   => $totalOriginal,
            'total_compressed_bytes' => $totalCompressed,
            'overall_ratio_percent'  => $overallRatio,
        ];
    }
}

$summary = new ArchiveCompressionSummary();
print_r($summary->summarize('/tmp/sample.zip'));

例7:非推奨関数からZipArchiveへの互換ラッパー

<?php

class CompressedSizeCompatWrapper
{
    /**
     * 古いzip_entry_compressedsize()相当のインターフェースを保ちつつ、
     * 内部実装だけをZipArchiveに切り替える互換レイヤー
     */
    public function getCompressedSize(string $zipPath, string $entryName): ?int
    {
        $zip = new ZipArchive();

        if ($zip->open($zipPath) !== true) {
            return null;
        }

        $stat = $zip->statName($entryName);
        $zip->close();

        return $stat !== false ? $stat['comp_size'] : null;
    }
}

$wrapper = new CompressedSizeCompatWrapper();
echo $wrapper->getCompressedSize('/tmp/sample.zip', 'readme.txt') . PHP_EOL;

関連関数との比較

関数/メソッド役割zip_entry_compressedsizeとの違い
zip_entry_compressedsize()手続き型インターフェースでエントリの圧縮後サイズを取得本記事の対象。PHP 7.2.0以降は非推奨
zip_entry_filesize()手続き型インターフェースでエントリの圧縮前サイズを取得対象が「圧縮前」のサイズである点が異なる
ZipArchive::statIndex()インデックス指定でエントリの統計情報を取得圧縮前後のサイズを含む複数の情報を一括取得できる
ZipArchive::statName()名前指定でエントリの統計情報を取得statIndex()と同様の情報を、名前で検索して取得する
ZipArchive::getFromName()エントリの中身そのものを取得サイズ情報ではなく、実際のファイル内容を扱う

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

  1. PHP 7.2.0以降でE_DEPRECATED警告が発生する これまでの記事と同様、この関数群を使用すると非推奨警告が発生します。早期の ZipArchive への移行が推奨されます。
  2. ZipArchiveではstat*()メソッドが複数の情報をまとめて返す 古いインターフェースでは、圧縮前サイズと圧縮後サイズをそれぞれ別の関数呼び出しで取得する必要がありましたが、ZipArchive::statIndex()statName() は、名前・サイズ・圧縮後サイズ・更新日時などをまとめて1回の呼び出しで取得できます(例2を参照)。
  3. 圧縮率がマイナスになる(サイズが増加する)ケースがある 既にJPEG、PNG、ZIPなど圧縮済みのファイルをさらにZIP圧縮すると、圧縮アルゴリズムのオーバーヘッドにより、圧縮後のサイズが圧縮前よりもわずかに大きくなることがあります。圧縮率の計算結果が負の値になる可能性も考慮しておきましょう(例5を参照)。
  4. ディレクトリエントリのサイズは特殊な扱いになる ZIPアーカイブ内のディレクトリを表すエントリは、通常サイズが0バイトです。全エントリのサイズを合計する処理を行う際は、このような0バイトのエントリが混在することを前提に実装しましょう(例4を参照)。
  5. 圧縮後サイズだけでは実際のディスク使用量と完全には一致しない ファイルシステムのブロックサイズなど、他の要因によって実際のディスク使用量は変動する可能性があります。zip_entry_compressedsize() やその代替が返す値は、あくまでアーカイブ内でのデータサイズであることを理解しておきましょう。

まとめ

観点まとめ
何をする関数かZIPエントリの圧縮後のバイト数を取得する(手続き型インターフェース)
現在の状態PHP 7.2.0以降で非推奨。将来的に削除される可能性がある
推奨される代替ZipArchive::statIndex() / statName()comp_size フィールド
主な用途圧縮率の計算、ディスク容量の見積もり、圧縮効率の低いエントリの検出
注意点圧縮率がマイナスになるケース、ディレクトリエントリの扱い、stat*()メソッドへの一本化

zip_entry_compressedsize() は、ZIPエントリの容量情報を取得するための、非推奨の手続き型関数です。ZipArchive::statIndex() などに移行することで、圧縮前後のサイズをはじめとする複数の統計情報を、より効率的にまとめて取得できるようになります。

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