PHPの基礎 - 圧縮・解凍
概要
PHPには、主に、Zlibライブラリを利用した圧縮機能が組み込まれている。
最も一般的なものは、gzcompress関数、gzuncompress関数があり、ZLIBフォーマットでデータを圧縮・解凍できる。
基本的な使用例を以下に示す。
$data = "圧縮したいテキストデータ";
$compressed = gzcompress($data);
$original = gzuncompress($compressed);
代替的な圧縮方式であるGZIPフォーマットを使用する場合は、gzencode関数およびgzdecode関数が使用できる。
これらは、HTTPでの転送に適している。
$compressed = gzencode($data);
$original = gzdecode($compressed);
大きなファイルを扱う場合、PHPは圧縮ストリームをサポートしている。
// 圧縮ファイルの書き込み
$gz = gzopen('file.gz', 'w9');
gzwrite($gz, $data);
gzclose($gz);
// 圧縮ファイルの読み込み
$gz = gzopen('file.gz', 'r');
$content = gzread($gz, 4096);
gzclose($gz);
複数のファイルを扱う場合は、ZIPアーカイブが便利である。
PHPのZIPアーカイブ操作では、ZipArchiveクラスを使用する。
$zip = new ZipArchive();
if ($zip->open('archive.zip', ZipArchive::CREATE) === TRUE) {
$zip->addFromString('test.txt', 'ファイルの内容');
$zip->addFile('existing.txt', 'new_name.txt');
$zip->close();
}
圧縮レベルは、0 (無圧縮) から9 (最大圧縮) まで指定可能である。
ただし、高い圧縮レベルはCPU負荷が高くなるため、状況に応じて適切なレベルを選択することが重要である。
また、バイナリデータを扱う場合は、データの破損を防ぐため、base64エンコードとの組み合わせを推奨する。
$compressed = base64_encode(gzcompress($data));
$original = gzuncompress(base64_decode($compressed));
これらの機能を組み合わせることにより、効率的なデータ圧縮と転送が実現できる。
特に大容量データの取り扱いやバックアップ処理等で重宝する。
圧縮
以下に示す例では、非同期処理のサポート、ストリーミング処理による効率的なメモリ使用、エラーハンドリングを実装している。
また、3つの圧縮形式 (ZIP、tar.gz、tar.xz) に対応している。
- declare(strict_types=1);
- これは、型の厳密なチェックを行うための宣言である。
- 例えば、string型を期待する引数に整数を渡した場合にエラーとなる。
- コードの信頼性を高めるために推奨されるが、必須ではない。
- チーム開発で型の一貫性を保つ場合、バグの早期発見を重視する場合、型の安全性を重視する場合は記述する。
- namespaceを使用する場合
- これは、名前空間を定義するための宣言である。
- コードの整理や名前の衝突を避けるために使用する。
- 大規模なプロジェクトやライブラリとして使用する場合に有効であるが、単独のスクリプトとして使用する場合は不要である。
- また、コードを再利用可能なライブラリとして提供する場合、オートローディングを使用する場合、他のライブラリとの名前衝突を避けたい場合等に記述する。
ZIP形式
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use ZipArchive;
use Generator;
/**
* ZIP形式で圧縮を行うメソッド
*
* @param string $sourcePath 圧縮対象のパス(ファイルまたはディレクトリ)
* @param string $destinationPath 出力先のZIPファイルパス
* @throws RuntimeException 圧縮処理中のエラー発生時
*/
function compressToZip(string $sourcePath, string $destinationPath): void
{
try {
$zip = new ZipArchive();
// ZIPファイルのオープン
if ($zip->open($destinationPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException('ZIPファイルの作成に失敗しました');
}
// ディレクトリの場合は再帰的に処理
if (is_dir($sourcePath)) {
$files = $this->getFilesRecursively($sourcePath);
foreach ($files as $file) {
$relativePath = substr($file, strlen($sourcePath) + 1);
$zip->addFile($file, $relativePath);
}
}
else {
// 単一ファイルの場合
$zip->addFile($sourcePath, basename($sourcePath));
}
$zip->close();
}
catch (\Exception $e) {
throw new RuntimeException("ZIP圧縮処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ディレクトリ内のファイルを再帰的に取得するプライベートメソッド
*
* @param string $dir 検索対象ディレクトリ
* @return Generator ファイルパスを返すジェネレータ
*/
function getFilesRecursively(string $dir): Generator
{
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($files as $file) {
yield $file->getRealPath();
}
}
// 使用例
$compression = new Utils\CompressionUtils();
try {
$compression->compressToZip('/path/to/source', '/path/to/output.zip');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
tar.gz形式
tar.gz圧縮を行う場合は、システムにtarコマンドがインストールされている必要がある。
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
/**
* tar.gz形式で圧縮を行うメソッド
*
* @param string $sourcePath 圧縮対象のパス
* @param string $destinationPath 出力先のtar.gzファイルパス
* @throws RuntimeException 圧縮処理中のエラー発生時
*/
function compressToTarGz(string $sourcePath, string $destinationPath): void
{
try {
// 一時的なTARファイルの作成
$tempTarFile = tempnam(sys_get_temp_dir(), 'tar');
// tarコマンドの実行
$sourceName = basename($sourcePath);
$sourceDir = dirname($sourcePath);
exec("cd {$sourceDir} && tar -cf {$tempTarFile} {$sourceName}", $output, $returnCode);
if ($returnCode !== 0) {
throw new RuntimeException('TAR作成に失敗しました');
}
// gzipでの圧縮処理
$gz = gzopen($destinationPath, 'wb9');
if ($gz === false) {
throw new RuntimeException('GZIPファイルのオープンに失敗しました');
}
// ストリーミング処理でTARファイルを読み込みながら圧縮
$handle = fopen($tempTarFile, 'rb');
while (!feof($handle)) {
gzwrite($gz, fread($handle, 8192));
}
fclose($handle);
gzclose($gz);
// 一時ファイルの削除
unlink($tempTarFile);
}
catch (\Exception $e) {
throw new RuntimeException("tar.gz圧縮処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ディレクトリ内のファイルを再帰的に取得するプライベートメソッド
*
* @param string $dir 検索対象ディレクトリ
* @return Generator ファイルパスを返すジェネレータ
*/
function getFilesRecursively(string $dir): Generator
{
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($files as $file) {
yield $file->getRealPath();
}
}
// 使用例
$compression = new Utils\CompressionUtils();
try {
// tar.gz圧縮の例
$compression->compressToTarGz('/path/to/source', '/path/to/output.tar.gz');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
tar.xz形式
tar.xz形式の圧縮には、PHPのXZ拡張機能が必要である。
また、tar.xz圧縮を行う場合は、システムにtarコマンドがインストールされている必要がある。
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
/**
* tar.xz形式で圧縮を行うメソッド
*
* @param string $sourcePath 圧縮対象のパス
* @param string $destinationPath 出力先のtar.xzファイルパス
* @throws RuntimeException 圧縮処理中のエラー発生時
*/
function compressToTarXz(string $sourcePath, string $destinationPath): void
{
try {
// PHPのXZ拡張機能が必要
if (!extension_loaded('xz')) {
throw new RuntimeException('XZ拡張機能がインストールされていません');
}
// 一時的なTARファイルの作成
$tempTarFile = tempnam(sys_get_temp_dir(), 'tar');
// tarコマンドの実行
$sourceName = basename($sourcePath);
$sourceDir = dirname($sourcePath);
exec("cd {$sourceDir} && tar -cf {$tempTarFile} {$sourceName}", $output, $returnCode);
if ($returnCode !== 0) {
throw new RuntimeException('TAR作成に失敗しました');
}
// XZ圧縮の実行
$xzStream = xzopen($destinationPath, 'wb9');
if ($xzStream === false) {
throw new RuntimeException('XZファイルのオープンに失敗しました');
}
// ストリーミング処理でTARファイルを読み込みながら圧縮
$handle = fopen($tempTarFile, 'rb');
while (!feof($handle)) {
xzwrite($xzStream, fread($handle, 8192));
}
fclose($handle);
xzclose($xzStream);
// 一時ファイルの削除
unlink($tempTarFile);
}
catch (\Exception $e) {
throw new RuntimeException("tar.xz圧縮処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ディレクトリ内のファイルを再帰的に取得するプライベートメソッド
*
* @param string $dir 検索対象ディレクトリ
* @return Generator ファイルパスを返すジェネレータ
*/
function getFilesRecursively(string $dir): Generator
{
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($files as $file) {
yield $file->getRealPath();
}
}
// 使用例
$compression = new Utils\CompressionUtils();
try {
// tar.xz圧縮の例
$compression->compressToTarXz('/path/to/source', '/path/to/output.tar.xz');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
非同期処理
/**
* 非同期でファイル圧縮を行うメソッド
*
* @param string $sourcePath 圧縮対象のパス
* @param string $destinationPath 出力先のパス
* @param string $type 圧縮形式('zip', 'tar.gz', 'tar.xz'のいずれか)
* @return Generator 圧縮の進捗状況を返すジェネレータ
*/
function compressAsync(string $sourcePath, string $destinationPath, string $type): Generator
{
yield '圧縮処理を開始します...';
try {
switch ($type) {
case 'zip':
$this->compressToZip($sourcePath, $destinationPath);
break;
case 'tar.gz':
$this->compressToTarGz($sourcePath, $destinationPath);
break;
case 'tar.xz':
$this->compressToTarXz($sourcePath, $destinationPath);
break;
default:
throw new RuntimeException('不正な圧縮形式が指定されました');
}
yield '圧縮処理が完了しました';
}
catch (\Exception $e) {
yield "圧縮処理中にエラーが発生しました: {$e->getMessage()}";
throw $e;
}
}
// 使用例
$compression = new Utils\CompressionUtils();
try {
// 非同期処理の例
// ZIP形式の場合
foreach ($compression->compressAsync('/path/to/source', '/path/to/output.zip', 'zip') as $status) {
echo $status . PHP_EOL;
}
// tar.gz形式の場合
foreach ($compression->compressAsync('/path/to/source', '/path/to/output.zip', 'zip') as $status) {
echo $status . PHP_EOL;
}
// tar.xz形式の場合
foreach ($compression->compressAsync('/path/to/source', '/path/to/output.zip', 'zip') as $status) {
echo $status . PHP_EOL;
}
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
解凍
以下に示す例では、非同期処理のサポート、ストリーミング処理による効率的なメモリ使用、エラーハンドリングを実装している。
また、3つの解凍形式 (ZIP、tar.gz、tar.xz) に対応している。
- declare(strict_types=1);
- これは、型の厳密なチェックを行うための宣言である。
- 例えば、string型を期待する引数に整数を渡した場合にエラーとなる。
- コードの信頼性を高めるために推奨されるが、必須ではない。
- チーム開発で型の一貫性を保つ場合、バグの早期発見を重視する場合、型の安全性を重視する場合は記述する。
- namespaceを使用する場合
- これは、名前空間を定義するための宣言である。
- コードの整理や名前の衝突を避けるために使用する。
- 大規模なプロジェクトやライブラリとして使用する場合に有効であるが、単独のスクリプトとして使用する場合は不要である。
- また、コードを再利用可能なライブラリとして提供する場合、オートローディングを使用する場合、他のライブラリとの名前衝突を避けたい場合等に記述する。
ZIP形式
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use ZipArchive;
use Generator;
/**
* ZIPファイルを解凍するメソッド
*
* @param string $sourceFile 解凍対象のZIPファイルパス
* @param string $destinationPath 解凍先のディレクトリパス
* @throws RuntimeException 解凍処理中のエラー発生時
*/
function extractZip(string $sourceFile, string $destinationPath): void
{
try {
// ファイルの検証
validateFile($sourceFile);
if (!file_exists($sourceFile)) {
throw new RuntimeException('指定されたZIPファイルが存在しません');
}
$zip = new ZipArchive();
$result = $zip->open($sourceFile);
if ($result !== true) {
throw new RuntimeException("ZIPファイルのオープンに失敗しました(エラーコード: {$result})");
}
// 解凍先ディレクトリが存在しない場合は作成
if (!file_exists($destinationPath)) {
if (!mkdir($destinationPath, 0777, true)) {
throw new RuntimeException('解凍先ディレクトリの作成に失敗しました');
}
}
if (!$zip->extractTo($destinationPath)) {
throw new RuntimeException('ZIPファイルの解凍に失敗しました');
}
$zip->close();
}
catch (\Exception $e) {
throw new RuntimeException("ZIP解凍処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ファイルの存在確認と権限チェックを行うプライベートメソッド
*
* @param string $filePath チェック対象のファイルパス
* @throws RuntimeException チェック失敗時
*/
function validateFile(string $filePath): void
{
if (!file_exists($filePath)) {
throw new RuntimeException("ファイルが存在しません: {$filePath}");
}
if (!is_readable($filePath)) {
throw new RuntimeException("ファイルの読み取り権限がありません: {$filePath}");
}
if (!is_file($filePath)) {
throw new RuntimeException("指定されたパスがファイルではありません: {$filePath}");
}
}
// 使用例
$decompression = new Utils\DecompressionUtils();
try {
// ZIP解凍の例
$decompression->extractZip('/path/to/archive.zip', '/path/to/destination');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
tar.gz形式
tar.gz圧縮を行う場合は、システムにtarコマンドがインストールされている必要がある。
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
/**
* tar.gzファイルを解凍するメソッド
*
* @param string $sourceFile 解凍対象のtar.gzファイルパス
* @param string $destinationPath 解凍先のディレクトリパス
* @throws RuntimeException 解凍処理中のエラー発生時
*/
function extractTarGz(string $sourceFile, string $destinationPath): void
{
try {
// ファイルの検証
validateFile($sourceFile);
if (!file_exists($sourceFile)) {
throw new RuntimeException('指定されたtar.gzファイルが存在しません');
}
// 解凍先ディレクトリの作成
if (!file_exists($destinationPath)) {
if (!mkdir($destinationPath, 0777, true)) {
throw new RuntimeException('解凍先ディレクトリの作成に失敗しました');
}
}
// 一時的なTARファイルの作成
$tempTarFile = tempnam(sys_get_temp_dir(), 'tar');
// gzipファイルの解凍(ストリーミング処理)
$gz = gzopen($sourceFile, 'rb');
if ($gz === false) {
throw new RuntimeException('GZIPファイルのオープンに失敗しました');
}
$tarHandle = fopen($tempTarFile, 'wb');
if ($tarHandle === false) {
throw new RuntimeException('一時ファイルの作成に失敗しました');
}
while (!gzeof($gz)) {
fwrite($tarHandle, gzread($gz, 8192));
}
gzclose($gz);
fclose($tarHandle);
// tarの展開
$currentDir = getcwd();
chdir($destinationPath);
exec("tar -xf {$tempTarFile}", $output, $returnCode);
chdir($currentDir);
if ($returnCode !== 0) {
throw new RuntimeException('TARファイルの展開に失敗しました');
}
// 一時ファイルの削除
unlink($tempTarFile);
}
catch (\Exception $e) {
throw new RuntimeException("tar.gz解凍処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ファイルの存在確認と権限チェックを行うプライベートメソッド
*
* @param string $filePath チェック対象のファイルパス
* @throws RuntimeException チェック失敗時
*/
function validateFile(string $filePath): void
{
if (!file_exists($filePath)) {
throw new RuntimeException("ファイルが存在しません: {$filePath}");
}
if (!is_readable($filePath)) {
throw new RuntimeException("ファイルの読み取り権限がありません: {$filePath}");
}
if (!is_file($filePath)) {
throw new RuntimeException("指定されたパスがファイルではありません: {$filePath}");
}
}
// 使用例
$decompression = new Utils\DecompressionUtils();
try {
// tar.gz解凍の例
$decompression->extractTarGz('/path/to/archive.tar.gz', '/path/to/destination');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
tar.xz形式
tar.xz形式の圧縮には、PHPのXZ拡張機能が必要である。
また、tar.xz圧縮を行う場合は、システムにtarコマンドがインストールされている必要がある。
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
/**
* tar.xzファイルを解凍するメソッド
*
* @param string $sourceFile 解凍対象のtar.xzファイルパス
* @param string $destinationPath 解凍先のディレクトリパス
* @throws RuntimeException 解凍処理中のエラー発生時
*/
function extractTarXz(string $sourceFile, string $destinationPath): void
{
try {
// ファイルの検証
validateFile($sourceFile);
if (!extension_loaded('xz')) {
throw new RuntimeException('XZ拡張機能がインストールされていません');
}
if (!file_exists($sourceFile)) {
throw new RuntimeException('指定されたtar.xzファイルが存在しません');
}
// 解凍先ディレクトリの作成
if (!file_exists($destinationPath)) {
if (!mkdir($destinationPath, 0777, true)) {
throw new RuntimeException('解凍先ディレクトリの作成に失敗しました');
}
}
// 一時的なTARファイルの作成
$tempTarFile = tempnam(sys_get_temp_dir(), 'tar');
// xzファイルの解凍(ストリーミング処理)
$xz = xzopen($sourceFile, 'rb');
if ($xz === false) {
throw new RuntimeException('XZファイルのオープンに失敗しました');
}
$tarHandle = fopen($tempTarFile, 'wb');
if ($tarHandle === false) {
throw new RuntimeException('一時ファイルの作成に失敗しました');
}
while (!xzeof($xz)) {
fwrite($tarHandle, xzread($xz, 8192));
}
xzclose($xz);
fclose($tarHandle);
// tarの展開
$currentDir = getcwd();
chdir($destinationPath);
exec("tar -xf {$tempTarFile}", $output, $returnCode);
chdir($currentDir);
if ($returnCode !== 0) {
throw new RuntimeException('TARファイルの展開に失敗しました');
}
// 一時ファイルの削除
unlink($tempTarFile);
}
catch (\Exception $e) {
throw new RuntimeException("tar.xz解凍処理中にエラーが発生しました: {$e->getMessage()}");
}
}
/**
* ファイルの存在確認と権限チェックを行うプライベートメソッド
*
* @param string $filePath チェック対象のファイルパス
* @throws RuntimeException チェック失敗時
*/
function validateFile(string $filePath): void
{
if (!file_exists($filePath)) {
throw new RuntimeException("ファイルが存在しません: {$filePath}");
}
if (!is_readable($filePath)) {
throw new RuntimeException("ファイルの読み取り権限がありません: {$filePath}");
}
if (!is_file($filePath)) {
throw new RuntimeException("指定されたパスがファイルではありません: {$filePath}");
}
}
// 使用例
$decompression = new Utils\DecompressionUtils();
try {
// tar.gz解凍の例
$decompression->extractTarXz('/path/to/archive.tar.xz', '/path/to/destination');
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}
非同期処理
/**
* 非同期でファイル解凍を行うメソッド
*
* @param string $sourceFile 解凍対象のファイルパス
* @param string $destinationPath 解凍先のディレクトリパス
* @param string $type 解凍形式('zip', 'tar.gz', 'tar.xz'のいずれか)
* @return Generator 解凍の進捗状況を返すジェネレータ
*/
function extractAsync(string $sourceFile, string $destinationPath, string $type): Generator
{
yield '解凍処理を開始します...';
try {
// ファイルの検証
validateFile($sourceFile);
switch ($type) {
case 'zip':
yield 'ZIPファイルの解凍を開始します...';
$this->extractZip($sourceFile, $destinationPath);
break;
case 'tar.gz':
yield 'tar.gzファイルの解凍を開始します...';
$this->extractTarGz($sourceFile, $destinationPath);
break;
case 'tar.xz':
yield 'tar.xzファイルの解凍を開始します...';
$this->extractTarXz($sourceFile, $destinationPath);
break;
default:
throw new RuntimeException('不正な解凍形式が指定されました');
}
yield '解凍処理が完了しました';
}
catch (\Exception $e) {
yield "解凍処理中にエラーが発生しました: {$e->getMessage()}";
throw $e;
}
}
/**
* ファイルの存在確認と権限チェックを行うプライベートメソッド
*
* @param string $filePath チェック対象のファイルパス
* @throws RuntimeException チェック失敗時
*/
function validateFile(string $filePath): void
{
if (!file_exists($filePath)) {
throw new RuntimeException("ファイルが存在しません: {$filePath}");
}
if (!is_readable($filePath)) {
throw new RuntimeException("ファイルの読み取り権限がありません: {$filePath}");
}
if (!is_file($filePath)) {
throw new RuntimeException("指定されたパスがファイルではありません: {$filePath}");
}
}
// 使用例
$decompression = new Utils\DecompressionUtils();
try {
// 非同期処理の例
// ZIP形式の場合
foreach ($decompression->extractAsync('/path/to/archive.zip', '/path/to/destination', 'zip') as $status) {
echo $status . PHP_EOL;
}
// tar.gz形式の場合
foreach ($decompression->extractAsync('/path/to/archive.tar.gz', '/path/to/destination', 'gz') as $status) {
echo $status . PHP_EOL;
}
// tar.xz形式の場合
foreach ($decompression->extractAsync('/path/to/archive.tar.xz', '/path/to/destination', 'xz') as $status) {
echo $status . PHP_EOL;
}
}
catch (RuntimeException $e) {
echo "エラーが発生しました: " . $e->getMessage();
}