はじめに
これまでの一連の記事で、非推奨の手続き型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()にはない、書き込み関連の機能 |
PharData | Phar/ZIP/TAR形式を扱うクラス | より広範なアーカイブ形式に対応する別のアプローチ |
よくある落とし穴(注意点)
- 戻り値の型チェックを
is_resource()で行う必要があるzip_open()は成功時にリソースを返しますが、失敗時には負の整数のエラーコードを返します。単純な真偽値としての判定(if ($zip))では正しく動作しないため、必ずis_resource()を使いましょう(例2を参照)。 - PHP 7.2.0以降でE_DEPRECATED警告が発生する これまでの記事と同様、この関数を使用すると非推奨警告が発生します。早期の
ZipArchiveへの移行が推奨されます。 - 読み取り専用であり、新規作成やファイル追加ができない
zip_open()を含む手続き型インターフェース全体に言えることですが、既存のZIPファイルを開いて中身を読むことしかできません。新しいZIPファイルを作成したり、既存のアーカイブに変更を加えたりする場合、最初からZipArchiveを使う必要があります(例5を参照)。 ZipArchive::open()は詳細なエラーコードを返すzip_open()の戻り値がエラーコードとして返す情報は限定的ですが、ZipArchive::open()はER_NOENT(ファイルなし)、ER_NOZIP(不正な形式)など、より詳細なエラー原因を返します。これにより、丁寧なエラーハンドリングが可能になります(例4を参照)。- シリーズ全体を通して見えてくる移行の一貫したメリット
zip_open()から始まる一連の手続き型関数群は、いずれも「明示的なリソース管理」「限定的な機能」「粗いエラーハンドリング」という共通の課題を抱えていました。ZipArchiveへの移行は、これらすべてを一度に解決してくれる、包括的な改善だと言えます(例6・例7を参照)。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | 指定したパスのZIPファイルを開き、以降のエントリ読み込み処理のためのリソースを取得する |
| 現在の状態 | PHP 7.2.0以降で非推奨。将来的に削除される可能性がある |
| 一連の処理における位置づけ | 手続き型ZIP処理全体の起点となる、最初のステップ |
| 推奨される代替 | ZipArchive::open()(読み取りに加え、新規作成・編集にも対応) |
| 注意点 | is_resource()による戻り値チェックの必要性、読み取り専用という機能的制約 |
zip_open() は、これまでの一連の記事で解説してきた非推奨の手続き型ZIP関数群すべての出発点です。ZipArchive::open() への移行によって、単なる非推奨警告の解消にとどまらず、詳細なエラーハンドリング、新規作成・編集機能、そしてシンプルで直感的なAPIという、多くのメリットを一度に得ることができます。ZIPアーカイブを扱うすべての新規開発において、最初から ZipArchive を選択することを強く推奨します。
