はじめに
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() | オブジェクト指向インターフェースでアーカイブを開く | 読み込み専用だった古いインターフェースと異なり、作成・編集も可能 |
PharData | Phar形式やZIP/TARを扱うクラス | より広範なアーカイブ形式に対応する、別のアプローチ |
よくある落とし穴(注意点)
- PHP 7.2.0以降でE_DEPRECATED警告が発生する
zip_close()を含む一連の古い関数を呼び出すと、非推奨警告がログに出力されます。error_reportingの設定によっては本番環境のログが汚染される原因になるため、早めの移行が望ましいです。 - 読み取り専用であり、ZIPファイルの作成・編集ができない 手続き型インターフェースは基本的にZIPファイルの「読み込み」専用です。新しいZIPファイルを作成したり、既存のアーカイブにファイルを追加したりする機能が必要な場合、そもそも
ZipArchiveを使わざるを得ません(例5を参照)。 - 将来的な関数削除に備える必要がある 非推奨関数は将来のメジャーバージョンで完全に削除される可能性があります。既存コードにこれらの関数が残っている場合、計画的な
ZipArchiveへの置き換えを検討しましょう(例6のような検出ツールが役立ちます)。 - 逐次アクセスのみで、ランダムアクセスができない
zip_read()は呼び出すたびに次のエントリへと進む「順次読み込み」専用であり、特定のエントリに直接アクセスすることはできません。ZipArchive::getNameIndex()やZipArchive::getFromName()のように、インデックスや名前を指定した直接アクセスが必要な場合、ZipArchiveの方が圧倒的に便利です(例2・例4を参照)。 - エラーハンドリングの粒度が粗い 古いインターフェースはエラー時の情報が限定的ですが、
ZipArchive::open()は失敗理由に応じた詳細なエラーコード(ER_NOENT、ER_NOZIPなど)を返すため、より丁寧なエラーハンドリングが可能です(例7を参照)。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | zip_open()で開いたZIPアーカイブのリソースを解放する(手続き型インターフェース) |
| 現在の状態 | PHP 7.2.0以降で非推奨。将来的に削除される可能性がある |
| 推奨される代替 | ZipArchiveクラス(open()、close()などのメソッド) |
| 機能面の制約 | 読み取り専用、順次アクセスのみ、エラー情報が限定的 |
| 移行の必要性 | 新規開発では使用を避け、既存コードは計画的にZipArchiveへ移行することが望ましい |
zip_close() を含む古い手続き型のZIP関数群は、PHPの歴史における初期のアーカイブ操作手段でしたが、現在は非推奨となっています。読み取り専用という機能的な制約に加え、将来の削除リスクも踏まえ、新規開発では最初から ZipArchive クラスを使うことを強く推奨します。
