Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions Service/DataMigrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Eccube\Common\EccubeConfig;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Logging\Middleware;
use wapmorgan\UnifiedArchive\UnifiedArchive;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class DataMigrationService
Expand Down Expand Up @@ -72,10 +71,8 @@ public function disableLogging(Connection $em)

public function setMigrationVersion($em, $tmpDir, $tmpFile)
{
$archive = UnifiedArchive::open($tmpDir . '/' . $tmpFile);
$fileNames = $archive->getFileNames();
// 解凍
$archive->extractFiles($tmpDir, $fileNames);
$archivePath = $tmpDir . '/' . $tmpFile;
$fileNames = $this->extractArchive($archivePath, $tmpDir);

// 圧縮方式の間違いに対応する
$path = pathinfo($fileNames[0]);
Expand Down Expand Up @@ -118,6 +115,66 @@ public function isVersion($version)
return $this->migrationVersion === $version;
}

/**
* アーカイブを解凍してファイル名一覧を返す
*
* tar/tar.gz は Archive_Tar、zip は ZipArchive を使用。
* EC-CUBE 2系バックアップの壊れた tar に対応するため PharData は使わない。
*
* @param string $archivePath アーカイブファイルのパス
* @param string $outputDir 解凍先ディレクトリ
* @return string[] 解凍されたファイル名一覧
*/
public function extractArchive(string $archivePath, string $outputDir): array
{
$ext = strtolower(pathinfo($archivePath, PATHINFO_EXTENSION));

if ($ext === 'zip') {
return $this->extractZip($archivePath, $outputDir);
}

// tar / tar.gz / tgz
return $this->extractTar($archivePath, $outputDir);
}

private function extractTar(string $archivePath, string $outputDir): array
{
$tar = new \Archive_Tar($archivePath);
$result = $tar->extract($outputDir);
if ($result === false) {
throw new \RuntimeException('アーカイブの解凍に失敗しました: ' . basename($archivePath));
}

$fileNames = [];
foreach ($tar->listContent() as $entry) {
$name = $entry['filename'];
// ディレクトリエントリ、macリソースフォーク、空エントリを除外
if ($name === '' || $name === './' || substr($name, -1) === '/' || strpos(basename($name), '._') === 0) {
continue;
}
$fileNames[] = $name;
}

return $fileNames;
}

private function extractZip(string $archivePath, string $outputDir): array
{
$zip = new \ZipArchive();
if ($zip->open($archivePath) !== true) {
throw new \RuntimeException('ZIPファイルの読み込みに失敗しました: ' . basename($archivePath));
}

$zip->extractTo($outputDir);
$fileNames = [];
for ($i = 0; $i < $zip->numFiles; $i++) {
$fileNames[] = $zip->getNameIndex($i);
}
$zip->close();

return $fileNames;
}

public function updateEnv($newMagicValue)
{
$projectDir = $this->params->get('kernel.project_dir');
Expand Down
108 changes: 108 additions & 0 deletions Tests/Service/ArchiveExtractionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace Plugin\DataMigration43\Tests\Service;

use Eccube\Tests\EccubeTestCase;
use Plugin\DataMigration43\Service\DataMigrationService;
use Symfony\Component\Filesystem\Filesystem;

class ArchiveExtractionTest extends EccubeTestCase
{
/** @var DataMigrationService */
private $service;

/** @var string */
private $fixturesDir;

/** @var string */
private $tmpDir;

public function setUp(): void
{
parent::setUp();
$this->service = self::getContainer()->get(DataMigrationService::class);
$this->fixturesDir = __DIR__ . '/../Fixtures/';
$this->tmpDir = sys_get_temp_dir() . '/datamigration_test_' . uniqid();
mkdir($this->tmpDir, 0777, true);
}

public function tearDown(): void
{
$fs = new Filesystem();
if (is_dir($this->tmpDir)) {
$fs->remove($this->tmpDir);
}
parent::tearDown();
}

public function tarGzProvider()
{
return [
'2.11系' => ['2_11_5.tar.gz', ['bkup_data.csv', 'autoinc_data.csv']],
'2.12系' => ['2_12_6.tar.gz', ['dtb_customer.csv', 'dtb_order.csv', 'dtb_member.csv']],
'2.13系' => ['2_13_5.tar.gz', ['dtb_customer.csv', 'dtb_order.csv', 'dtb_member.csv']],
'3.0.9' => ['3_0_9.tar.gz', ['dtb_product.csv', 'dtb_customer.csv']],
'3.0.18' => ['3_0_18.tar.gz', ['dtb_product.csv', 'dtb_customer.csv']],
'4.0系' => ['4_0_6.tar.gz', ['dtb_order_item.csv', 'dtb_customer.csv']],
'4.1系' => ['4_1_2.tar.gz', ['dtb_order_item.csv', 'dtb_customer.csv']],
'member_test' => ['member_test.tar.gz', ['dtb_member.csv', 'mtb_authority.csv']],
];
}

/**
* @dataProvider tarGzProvider
*/
public function testTarGz解凍(string $filename, array $expectedFiles)
{
$archivePath = $this->fixturesDir . $filename;
$fileNames = $this->service->extractArchive($archivePath, $this->tmpDir);

self::assertNotEmpty($fileNames, $filename . ' のファイル一覧が空');

// 期待するファイルが解凍されているか確認
foreach ($expectedFiles as $expected) {
$found = false;
// サブディレクトリ内も検索
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->tmpDir, \FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->getFilename() === $expected) {
$found = true;
break;
}
}
self::assertTrue($found, $filename . ' から ' . $expected . ' が解凍されていること');
}
}

public function testZip解凍()
{
// テスト用ZIPを作成
$zipPath = $this->tmpDir . '/test.zip';
$zip = new \ZipArchive();
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('dtb_customer.csv', "id,name\n1,test\n");
$zip->addFromString('dtb_product.csv', "id,name\n1,product\n");
$zip->close();

$outputDir = $this->tmpDir . '/output';
mkdir($outputDir, 0777, true);

$fileNames = $this->service->extractArchive($zipPath, $outputDir);

self::assertContains('dtb_customer.csv', $fileNames);
self::assertContains('dtb_product.csv', $fileNames);
self::assertFileExists($outputDir . '/dtb_customer.csv');
self::assertFileExists($outputDir . '/dtb_product.csv');
}

public function test不正なファイルで例外()
{
$badFile = $this->tmpDir . '/bad.tar.gz';
file_put_contents($badFile, 'not an archive');

$this->expectException(\RuntimeException::class);
$this->service->extractArchive($badFile, $this->tmpDir);
}
}
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"ec-cube/plugin-installer": "^2.0",
"pear/pear-core-minimal" : "^1.10.10",
"pear/archive_tar" : "^1.4.13",
"wapmorgan/unified-archive": "^1.1.1",
"nobuhiko/bulk-insert-query": "^1.1"
},
"extra": {
Expand Down