[PHP]zip_closeとは?非推奨の手続き型ZIP関数とZipArchiveへの移行を徹底解説

PHP

はじめに

PHPには、ZIPアーカイブを扱うための機能が2つの世代にわたって存在します。1つは zip_open()、zip_read()、zip_close() といった古い手続き型の関数群、もう1つは現在標準的に使われている ZipArchive クラスです。今回取り上げる zip_close() は、前者の古い手続き型関数群の1つで、zip_open() で開いたZIPアーカイブのリソースを閉じるための関数です。

まず押さえておくべき重要な点として、zip_open()、zip_read()、zip_close() などの手続き型ZIP関数群は、PHP 7.2.0以降で非推奨(deprecated)とされており、将来的に削除される可能性があります。現在アクティブに開発するプロジェクトであれば、オブジェクト指向の ZipArchive クラスを使うことが強く推奨されています。本記事では、zip_close() を含む古い関数群の役割と、ZipArchive への移行方法を詳しく解説します。


関数概要

項目内容
関数名zip_close()
所属拡張Zip拡張(手続き型インターフェース)
シグネチャzip_close(resource $zip): void
引数$zip — zip_open() で取得したZIPアーカイブのリソース
戻り値なし(void)
対応バージョンPHP 4.1.0以降。PHP 7.2.0以降は非推奨
対になる関数zip_open()(アーカイブを開く)
推奨される代替ZipArchive::close()(オブジェクト指向インターフェース)

新旧インターフェースの関係(イメージ図)

  【古い手続き型インターフェース(非推奨)】
  zip_open($path)         ← ZIPファイルを開く(リソースを返す)
        │
        ▼
  zip_read($zipResource)  ← エントリを1つずつ順番に読み込む
        │
        ▼
  zip_close($zipResource) ← ★この記事の対象。リソースを解放する


  【現在推奨されるオブジェクト指向インターフェース】
  $zip = new ZipArchive();
  $zip->open($path);       ← ZIPファイルを開く
        │
        ▼
  $zip->getNameIndex($i);  ← エントリに自由にアクセス(インデックス指定も可能)
        │
        ▼
  $zip->close();           ← アーカイブを閉じる

ポイントは、zip_close() を含む手続き型のインターフェースが、ZIPファイルの内容を先頭から順番にしか読み進められない、逐次アクセス型の設計であったのに対し、ZipArchive はより柔軟にエントリへアクセスでき、書き込みやエントリの追加・削除といった操作にも対応している点です。この機能の違いも、ZipArchive への移行が推奨される理由の一つです。


実践サンプル7選

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

<?php

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

        if (is_resource($zip)) {
            while ($entry = zip_read($zip)) {
                $entries[] = zip_entry_name($entry);
            }
            // 使い終わったリソースを解放する
            zip_close($zip);
        }

        return $entries;
    }
}

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

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

<?php

class ModernZipReader
{
    /**
     * ZipArchiveクラスを使った、現在推奨される実装
     */
    public function readEntries(string $zipPath): array
    {
        $entries = [];
        $zip = new ZipArchive();

        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $entries[] = $zip->getNameIndex($i);
            }
            $zip->close();
        }

        return $entries;
    }
}

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

例3:非推奨警告を検知して移行を促すラッパークラス

<?php

class DeprecationAwareZipHandler
{
    /**
     * PHPバージョンを確認し、
     * 非推奨の古い関数群の使用を避けるようにする
     */
    public function readEntriesSafely(string $zipPath): array
    {
        if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
            return $this->readWithZipArchive($zipPath);
        }

        // 古いPHPバージョンとの互換性維持が必要な場合のフォールバック
        return $this->readWithLegacyFunctions($zipPath);
    }

    private function readWithZipArchive(string $zipPath): array
    {
        $entries = [];
        $zip = new ZipArchive();
        if ($zip->open($zipPath) === true) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $entries[] = $zip->getNameIndex($i);
            }
            $zip->close();
        }
        return $entries;
    }

    private function readWithLegacyFunctions(string $zipPath): array
    {
        $entries = [];
        $zip = zip_open($zipPath);
        if (is_resource($zip)) {
            while ($entry = zip_read($zip)) {
                $entries[] = zip_entry_name($entry);
            }
            zip_close($zip);
        }
        return $entries;
    }
}

例4:ZipArchiveでファイルの中身を展開して読み込むクラス

<?php

class ZipContentExtractor
{
    /**
     * ZipArchiveを使い、特定エントリの中身をメモリ上で読み取る
     * (古いzip_entry_read()に相当する処理)
     */
    public function readFileContent(string $zipPath, string $entryName): ?string
    {
        $zip = new ZipArchive();

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

        $content = $zip->getFromName($entryName);
        $zip->close();

        return $content !== false ? $content : null;
    }
}

$extractor = new ZipContentExtractor();
echo $extractor->readFileContent('/tmp/sample.zip', 'readme.txt') . PHP_EOL;

例5:ZipArchiveで新しいZIPファイルを作成するクラス

<?php

class ZipArchiveCreator
{
    /**
     * 古い手続き型インターフェースにはZIP作成機能がなく、
     * この点もZipArchiveへの移行が必須となる理由の一つ
     */
    public function createArchive(string $outputPath, array $files): bool
    {
        $zip = new ZipArchive();

        if ($zip->open($outputPath, ZipArchive::CREATE) !== true) {
            return false;
        }

        foreach ($files as $localName => $filePath) {
            $zip->addFile($filePath, $localName);
        }

        return $zip->close();
    }
}

$creator = new ZipArchiveCreator();
// $creator->createArchive('/tmp/output.zip', ['readme.txt' => '/tmp/source/readme.txt']);

例6:既存コードベースからの移行チェックリストを検証するツール

<?php

class LegacyZipUsageDetector
{
    /**
     * ソースコード内に非推奨のzip_*関数が
     * 使われていないかを検出する簡易ツール
     */
    private const DEPRECATED_FUNCTIONS = [
        'zip_open', 'zip_read', 'zip_close',
        'zip_entry_open', 'zip_entry_read', 'zip_entry_close',
        'zip_entry_name', 'zip_entry_filesize',
    ];

    public function scanFile(string $phpFilePath): array
    {
        $content = file_get_contents($phpFilePath);
        $found = [];

        foreach (self::DEPRECATED_FUNCTIONS as $func) {
            if (preg_match('/\b' . preg_quote($func, '/') . '\s*\(/', $content)) {
                $found[] = $func;
            }
        }

        return $found;
    }
}

$detector = new LegacyZipUsageDetector();
// print_r($detector->scanFile('/path/to/legacy_code.php'));

例7:エラーハンドリングを含めた堅牢なZipArchive実装

<?php

class RobustZipOpener
{
    /**
     * ZipArchive::open()の戻り値を詳細にチェックし、
     * 失敗理由に応じた例外をスローする
     */
    public function openStrict(string $zipPath): ZipArchive
    {
        $zip = new ZipArchive();
        $result = $zip->open($zipPath);

        if ($result !== true) {
            $errorMessages = [
                ZipArchive::ER_NOENT  => 'ファイルが存在しません',
                ZipArchive::ER_NOZIP  => '有効なZIPアーカイブではありません',
                ZipArchive::ER_INCONS => 'アーカイブに矛盾があります',
            ];
            $message = $errorMessages[$result] ?? "不明なエラー(コード: {$result})";
            throw new RuntimeException("ZIPファイルを開けませんでした: {$message}");
        }

        return $zip;
    }
}

$opener = new RobustZipOpener();
try {
    $zip = $opener->openStrict('/tmp/sample.zip');
    echo "ファイル数: {$zip->numFiles}" . PHP_EOL;
    $zip->close();
} catch (RuntimeException $e) {
    echo 'エラー: ' . $e->getMessage() . PHP_EOL;
}

関連関数との比較

関数/クラス役割zip_closeとの違い
zip_close()手続き型インターフェースでZIPリソースを解放本記事の対象。PHP 7.2.0以降は非推奨
zip_open()手続き型インターフェースでZIPファイルを開くzip_close()と対になる、同じく非推奨の関数
ZipArchive::close()オブジェクト指向インターフェースでアーカイブを閉じる現在推奨される代替。書き込みも保存される
ZipArchive::open()オブジェクト指向インターフェースでアーカイブを開く読み込み専用だった古いインターフェースと異なり、作成・編集も可能
PharDataPhar形式やZIP/TARを扱うクラスより広範なアーカイブ形式に対応する、別のアプローチ

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

  1. PHP 7.2.0以降でE_DEPRECATED警告が発生する zip_close() を含む一連の古い関数を呼び出すと、非推奨警告がログに出力されます。error_reporting の設定によっては本番環境のログが汚染される原因になるため、早めの移行が望ましいです。
  2. 読み取り専用であり、ZIPファイルの作成・編集ができない 手続き型インターフェースは基本的にZIPファイルの「読み込み」専用です。新しいZIPファイルを作成したり、既存のアーカイブにファイルを追加したりする機能が必要な場合、そもそも ZipArchive を使わざるを得ません(例5を参照)。
  3. 将来的な関数削除に備える必要がある 非推奨関数は将来のメジャーバージョンで完全に削除される可能性があります。既存コードにこれらの関数が残っている場合、計画的な ZipArchive への置き換えを検討しましょう(例6のような検出ツールが役立ちます)。
  4. 逐次アクセスのみで、ランダムアクセスができない zip_read() は呼び出すたびに次のエントリへと進む「順次読み込み」専用であり、特定のエントリに直接アクセスすることはできません。ZipArchive::getNameIndex() や ZipArchive::getFromName() のように、インデックスや名前を指定した直接アクセスが必要な場合、ZipArchive の方が圧倒的に便利です(例2・例4を参照)。
  5. エラーハンドリングの粒度が粗い 古いインターフェースはエラー時の情報が限定的ですが、ZipArchive::open() は失敗理由に応じた詳細なエラーコード(ER_NOENT、ER_NOZIPなど)を返すため、より丁寧なエラーハンドリングが可能です(例7を参照)。

まとめ

観点まとめ
何をする関数かzip_open()で開いたZIPアーカイブのリソースを解放する(手続き型インターフェース)
現在の状態PHP 7.2.0以降で非推奨。将来的に削除される可能性がある
推奨される代替ZipArchiveクラス(open()、close()などのメソッド)
機能面の制約読み取り専用、順次アクセスのみ、エラー情報が限定的
移行の必要性新規開発では使用を避け、既存コードは計画的にZipArchiveへ移行することが望ましい

zip_close() を含む古い手続き型のZIP関数群は、PHPの歴史における初期のアーカイブ操作手段でしたが、現在は非推奨となっています。読み取り専用という機能的な制約に加え、将来の削除リスクも踏まえ、新規開発では最初から ZipArchive クラスを使うことを強く推奨します。

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