[PHP]zip_openとは?非推奨のZIPアーカイブオープン関数とZipArchiveへの移行を徹底解説

PHP

はじめに

これまでの一連の記事で、非推奨の手続き型ZIP関数群を数多く解説してきました。それらすべての記事のサンプルコードで繰り返し登場していたのが、今回取り上げる zip_open() です。この関数こそが、一連の手続き型ZIP処理における最初のステップ、つまりZIPアーカイブファイルを開く役割を担っています。

zip_open() は、指定したパスのZIPファイルを開き、以降 zip_read() で各エントリを順番に取得していくための「ハンドル」となるリソースを返します。これまでの記事で解説してきた zip_close()、zip_entry_open()、zip_entry_read() など、すべての手続き型ZIP関数がこの zip_open() から始まる一連の流れの中に位置づけられます。前回までと同様、この関数もPHP 7.2.0以降で非推奨とされており、現在は ZipArchive クラスの利用が推奨されています。本記事では、この関数の役割と、一連のシリーズの締めくくりとして、ZipArchive への移行の全体像を改めて整理します。


関数概要

項目内容
関数名zip_open()
所属拡張Zip拡張(手続き型インターフェース)
シグネチャzip_open(string $filename): resource|int|false
引数$filename — 開くZIPファイルのパス
戻り値成功時はリソース、失敗時はエラーコード(負の整数)またはfalse
対応バージョンPHP 4.1.0以降。PHP 7.2.0以降は非推奨
対になる関数zip_close()(アーカイブを閉じる)
推奨される代替ZipArchive::open()

一連のシリーズ全体像(イメージ図)

  【非推奨の手続き型インターフェース全体の流れ】

  zip_open($path)              ★この記事の対象(起点)
        │
        ▼
  zip_read($zip)                    ← エントリを1つ取得
        │
        ▼
  zip_entry_name($entry)            ← エントリ名を取得(記事あり)
  zip_entry_filesize($entry)        ← 圧縮前サイズを取得(記事あり)
  zip_entry_compressedsize($entry)  ← 圧縮後サイズを取得(記事あり)
  zip_entry_compressionmethod($entry) ← 圧縮方式を取得(記事あり)
        │
        ▼
  zip_entry_open($zip, $entry)      ← エントリを開く(記事あり)
        │
        ▼
  zip_entry_read($entry, $size)     ← 内容を読み込む(記事あり)
        │
        ▼
  zip_entry_close($entry)           ← エントリを閉じる(記事あり)
        │
        ▼
  zip_close($zip)                   ← アーカイブを閉じる(記事あり)

ポイントは、zip_open() がこのシリーズすべての出発点であるという点です。この関数が返すリソースがなければ、zip_read() 以降のどの処理も実行できません。まさに、非推奨の手続き型ZIP処理全体を支える土台と言える関数です。


実践サンプル7選

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

<?php

class LegacyZipOpener
{
    /**
     * 注意: PHP 7.2.0以降、この一連の関数は非推奨です
     * 新規開発ではZipArchiveの使用を強く推奨します
     */
    public function openAndListEntries(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;
    }
}

$opener = new LegacyZipOpener();
print_r($opener->openAndListEntries('/tmp/sample.zip'));

例2:戻り値が整数(エラーコード)になるケースを正しく処理する

<?php

class ErrorCodeAwareOpener
{
    /**
     * zip_open()は失敗時にリソースではなく、
     * 負の整数のエラーコードを返すことがある
     * (is_resource()での判定が重要な理由)
     */
    public function openWithErrorHandling(string $zipPath): array
    {
        $result = zip_open($zipPath);

        if (is_resource($result)) {
            zip_close($result);
            return ['success' => true];
        }

        // is_resource()がfalseの場合、$resultは整数のエラーコード
        return ['success' => false, 'error_code' => $result];
    }
}

$opener = new ErrorCodeAwareOpener();
print_r($opener->openWithErrorHandling('/tmp/nonexistent.zip'));

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

<?php

class ModernZipOpener
{
    /**
     * ZipArchive::open()は、
     * 成功時にtrue、失敗時には詳細なエラーコード(整数)を返す
     * より明確な設計になっている
     */
    public function openAndListEntries(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;
    }
}

$opener = new ModernZipOpener();
print_r($opener->openAndListEntries('/tmp/sample.zip'));

例4:詳細なエラーコードを扱う堅牢な実装

<?php

class DetailedErrorHandlingOpener
{
    /**
     * ZipArchive::open()が返す詳細なエラーコードを
     * 人間が読めるメッセージに変換する
     */
    private const ERROR_MESSAGES = [
        ZipArchive::ER_NOENT  => 'ファイルが見つかりません',
        ZipArchive::ER_NOZIP  => '有効なZIPアーカイブではありません',
        ZipArchive::ER_INCONS => 'アーカイブの内容に矛盾があります',
        ZipArchive::ER_MEMORY => 'メモリの確保に失敗しました',
        ZipArchive::ER_READ   => '読み込みエラーが発生しました',
    ];

    public function openStrict(string $zipPath): ZipArchive
    {
        $zip = new ZipArchive();
        $result = $zip->open($zipPath);

        if ($result !== true) {
            $message = self::ERROR_MESSAGES[$result] ?? "不明なエラー(コード: {$result})";
            throw new RuntimeException("ZIPを開けませんでした: {$message}");
        }

        return $zip;
    }
}

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

例5:新規ZIPファイルを作成するモード(旧方式にはない機能)

<?php

class ZipCreationCapability
{
    /**
     * zip_open()は読み取り専用だが、
     * ZipArchive::open()はフラグ指定により新規作成・追記も可能
     */
    public function createOrOpenForWriting(string $zipPath): ZipArchive
    {
        $zip = new ZipArchive();

        // CREATEフラグにより、存在しない場合は新規作成される
        $result = $zip->open($zipPath, ZipArchive::CREATE);

        if ($result !== true) {
            throw new RuntimeException('ZIPアーカイブの作成/オープンに失敗しました');
        }

        return $zip;
    }
}

$capability = new ZipCreationCapability();
$zip = $capability->createOrOpenForWriting('/tmp/new_archive.zip');
$zip->addFromString('hello.txt', 'Hello, World!');
$zip->close();

例6:一連の処理全体を通したbefore/after比較のまとめ

<?php

class FullMigrationSummary
{
    /**
     * このシリーズ全体で紹介してきた非推奨関数群と
     * ZipArchiveの対応関係を一覧化する
     */
    public function getComparisonTable(): array
    {
        return [
            'zip_open()'                     => 'ZipArchive::open()',
            'zip_read()'                     => '$zip->numFiles によるループ処理',
            'zip_entry_name()'               => '$zip->getNameIndex($i)',
            'zip_entry_filesize()'           => "\$zip->statIndex(\$i)['size']",
            'zip_entry_compressedsize()'     => "\$zip->statIndex(\$i)['comp_size']",
            'zip_entry_compressionmethod()'  => "\$zip->statIndex(\$i)['comp_method']",
            'zip_entry_open() + zip_entry_read()' => '$zip->getFromIndex($i)',
            'zip_entry_close()'              => '(内部で自動処理、不要)',
            'zip_close()'                    => '$zip->close()',
        ];
    }
}

$summary = new FullMigrationSummary();
foreach ($summary->getComparisonTable() as $legacy => $modern) {
    echo "{$legacy}\n  → {$modern}\n\n";
}

例7:シリーズの集大成としての完全な移行済み実装

<?php

class CompleteZipArchiveReader
{
    /**
     * これまでの記事で紹介してきたすべての情報を統合した、
     * ZipArchiveベースの完全な読み込みクラス
     */
    public function readArchiveDetails(string $zipPath): array
    {
        $zip = new ZipArchive();
        $result = $zip->open($zipPath);

        if ($result !== true) {
            throw new RuntimeException("ZIPを開けませんでした(コード: {$result})");
        }

        $details = [];
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $stat = $zip->statIndex($i);
            $details[] = [
                'name'             => $stat['name'],
                'original_size'    => $stat['size'],
                'compressed_size'  => $stat['comp_size'],
                'compression_method' => $stat['comp_method'],
                'is_directory'     => str_ends_with($stat['name'], '/'),
            ];
        }

        $zip->close();
        return $details;
    }
}

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

関連関数との比較

関数/メソッド役割zip_openとの違い
zip_open()手続き型でZIPアーカイブを開く(起点)本記事の対象。PHP 7.2.0以降は非推奨
zip_close()手続き型でアーカイブを閉じるzip_open()と対になる関数(別記事で解説済み)
ZipArchive::open()オブジェクト指向でアーカイブを開く読み取り専用の旧方式と異なり、新規作成・追記も可能
ZipArchive::CREATE(定数)存在しない場合に新規作成するフラグzip_open()にはない、書き込み関連の機能
PharDataPhar/ZIP/TAR形式を扱うクラスより広範なアーカイブ形式に対応する別のアプローチ

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

  1. 戻り値の型チェックをis_resource()で行う必要がある zip_open() は成功時にリソースを返しますが、失敗時には負の整数のエラーコードを返します。単純な真偽値としての判定(if ($zip))では正しく動作しないため、必ず is_resource() を使いましょう(例2を参照)。
  2. PHP 7.2.0以降でE_DEPRECATED警告が発生する これまでの記事と同様、この関数を使用すると非推奨警告が発生します。早期の ZipArchive への移行が推奨されます。
  3. 読み取り専用であり、新規作成やファイル追加ができない zip_open() を含む手続き型インターフェース全体に言えることですが、既存のZIPファイルを開いて中身を読むことしかできません。新しいZIPファイルを作成したり、既存のアーカイブに変更を加えたりする場合、最初から ZipArchive を使う必要があります(例5を参照)。
  4. ZipArchive::open()は詳細なエラーコードを返す zip_open() の戻り値がエラーコードとして返す情報は限定的ですが、ZipArchive::open() は ER_NOENT(ファイルなし)、ER_NOZIP(不正な形式)など、より詳細なエラー原因を返します。これにより、丁寧なエラーハンドリングが可能になります(例4を参照)。
  5. シリーズ全体を通して見えてくる移行の一貫したメリット zip_open() から始まる一連の手続き型関数群は、いずれも「明示的なリソース管理」「限定的な機能」「粗いエラーハンドリング」という共通の課題を抱えていました。ZipArchive への移行は、これらすべてを一度に解決してくれる、包括的な改善だと言えます(例6・例7を参照)。

まとめ

観点まとめ
何をする関数か指定したパスのZIPファイルを開き、以降のエントリ読み込み処理のためのリソースを取得する
現在の状態PHP 7.2.0以降で非推奨。将来的に削除される可能性がある
一連の処理における位置づけ手続き型ZIP処理全体の起点となる、最初のステップ
推奨される代替ZipArchive::open()(読み取りに加え、新規作成・編集にも対応)
注意点is_resource()による戻り値チェックの必要性、読み取り専用という機能的制約

zip_open() は、これまでの一連の記事で解説してきた非推奨の手続き型ZIP関数群すべての出発点です。ZipArchive::open() への移行によって、単なる非推奨警告の解消にとどまらず、詳細なエラーハンドリング、新規作成・編集機能、そしてシンプルで直感的なAPIという、多くのメリットを一度に得ることができます。ZIPアーカイブを扱うすべての新規開発において、最初から ZipArchive を選択することを強く推奨します。

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