「PHPの基礎 - 圧縮・解凍」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
87行目: 87行目:
<br>
<br>
  <syntaxhighlight lang="php">
  <syntaxhighlight lang="php">
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
  /**
  /**
   * tar.gz形式で圧縮を行うメソッド
   * tar.gz形式で圧縮を行うメソッド
167行目: 174行目:
<br>
<br>
  <syntaxhighlight lang="php">
  <syntaxhighlight lang="php">
declare(strict_types=1);
namespace Utils;
use RuntimeException;
use Generator;
  /**
  /**
   * tar.xz形式で圧縮を行うメソッド
   * tar.xz形式で圧縮を行うメソッド

2024年11月9日 (土) 18:38時点における版

概要



圧縮

以下に示す例では、非同期処理のサポート、ストリーミング処理による効率的なメモリ使用、エラーハンドリングを実装している。
また、3つの圧縮形式 (ZIP、tar.gz、tar.xz) に対応している。

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();
 }