Skip to content

Commit aa8d3cf

Browse files
authored
Merge pull request #30 from nobuhiko/refactor/remove-unified-archive
refactor: UnifiedArchiveを除去しArchive_Tar+ZipArchiveに置換
2 parents af47c3f + 3861387 commit aa8d3cf

3 files changed

Lines changed: 170 additions & 6 deletions

File tree

Service/DataMigrationService.php

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Eccube\Common\EccubeConfig;
66
use Doctrine\DBAL\Connection;
77
use Doctrine\DBAL\Logging\Middleware;
8-
use wapmorgan\UnifiedArchive\UnifiedArchive;
98
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
109

1110
class DataMigrationService
@@ -78,10 +77,8 @@ public function disableLogging(Connection $em)
7877

7978
public function setMigrationVersion($em, $tmpDir, $tmpFile)
8079
{
81-
$archive = UnifiedArchive::open($tmpDir . '/' . $tmpFile);
82-
$fileNames = $archive->getFileNames();
83-
// 解凍
84-
$archive->extractFiles($tmpDir, $fileNames);
80+
$archivePath = $tmpDir . '/' . $tmpFile;
81+
$fileNames = $this->extractArchive($archivePath, $tmpDir);
8582

8683
// 圧縮方式の間違いに対応する
8784
$path = pathinfo($fileNames[0]);
@@ -124,6 +121,66 @@ public function isVersion($version)
124121
return $this->migrationVersion === $version;
125122
}
126123

124+
/**
125+
* アーカイブを解凍してファイル名一覧を返す
126+
*
127+
* tar/tar.gz は Archive_Tar、zip は ZipArchive を使用。
128+
* EC-CUBE 2系バックアップの壊れた tar に対応するため PharData は使わない。
129+
*
130+
* @param string $archivePath アーカイブファイルのパス
131+
* @param string $outputDir 解凍先ディレクトリ
132+
* @return string[] 解凍されたファイル名一覧
133+
*/
134+
public function extractArchive(string $archivePath, string $outputDir): array
135+
{
136+
$ext = strtolower(pathinfo($archivePath, PATHINFO_EXTENSION));
137+
138+
if ($ext === 'zip') {
139+
return $this->extractZip($archivePath, $outputDir);
140+
}
141+
142+
// tar / tar.gz / tgz
143+
return $this->extractTar($archivePath, $outputDir);
144+
}
145+
146+
private function extractTar(string $archivePath, string $outputDir): array
147+
{
148+
$tar = new \Archive_Tar($archivePath);
149+
$result = $tar->extract($outputDir);
150+
if ($result === false) {
151+
throw new \RuntimeException('アーカイブの解凍に失敗しました: ' . basename($archivePath));
152+
}
153+
154+
$fileNames = [];
155+
foreach ($tar->listContent() as $entry) {
156+
$name = $entry['filename'];
157+
// ディレクトリエントリ、macリソースフォーク、空エントリを除外
158+
if ($name === '' || $name === './' || substr($name, -1) === '/' || strpos(basename($name), '._') === 0) {
159+
continue;
160+
}
161+
$fileNames[] = $name;
162+
}
163+
164+
return $fileNames;
165+
}
166+
167+
private function extractZip(string $archivePath, string $outputDir): array
168+
{
169+
$zip = new \ZipArchive();
170+
if ($zip->open($archivePath) !== true) {
171+
throw new \RuntimeException('ZIPファイルの読み込みに失敗しました: ' . basename($archivePath));
172+
}
173+
174+
$zip->extractTo($outputDir);
175+
$fileNames = [];
176+
for ($i = 0; $i < $zip->numFiles; $i++) {
177+
$fileNames[] = $zip->getNameIndex($i);
178+
}
179+
$zip->close();
180+
181+
return $fileNames;
182+
}
183+
127184
public function updateEnv($newMagicValue)
128185
{
129186
// 改行を除去し、安全な文字のみ許可(.envインジェクション対策)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace Plugin\DataMigration43\Tests\Service;
4+
5+
use Eccube\Tests\EccubeTestCase;
6+
use Plugin\DataMigration43\Service\DataMigrationService;
7+
use Symfony\Component\Filesystem\Filesystem;
8+
9+
class ArchiveExtractionTest extends EccubeTestCase
10+
{
11+
/** @var DataMigrationService */
12+
private $service;
13+
14+
/** @var string */
15+
private $fixturesDir;
16+
17+
/** @var string */
18+
private $tmpDir;
19+
20+
public function setUp(): void
21+
{
22+
parent::setUp();
23+
$this->service = self::getContainer()->get(DataMigrationService::class);
24+
$this->fixturesDir = __DIR__ . '/../Fixtures/';
25+
$this->tmpDir = sys_get_temp_dir() . '/datamigration_test_' . uniqid();
26+
mkdir($this->tmpDir, 0777, true);
27+
}
28+
29+
public function tearDown(): void
30+
{
31+
$fs = new Filesystem();
32+
if (is_dir($this->tmpDir)) {
33+
$fs->remove($this->tmpDir);
34+
}
35+
parent::tearDown();
36+
}
37+
38+
public function tarGzProvider()
39+
{
40+
return [
41+
'2.11系' => ['2_11_5.tar.gz', ['bkup_data.csv', 'autoinc_data.csv']],
42+
'2.12系' => ['2_12_6.tar.gz', ['dtb_customer.csv', 'dtb_order.csv', 'dtb_member.csv']],
43+
'2.13系' => ['2_13_5.tar.gz', ['dtb_customer.csv', 'dtb_order.csv', 'dtb_member.csv']],
44+
'3.0.9' => ['3_0_9.tar.gz', ['dtb_product.csv', 'dtb_customer.csv']],
45+
'3.0.18' => ['3_0_18.tar.gz', ['dtb_product.csv', 'dtb_customer.csv']],
46+
'4.0系' => ['4_0_6.tar.gz', ['dtb_order_item.csv', 'dtb_customer.csv']],
47+
'4.1系' => ['4_1_2.tar.gz', ['dtb_order_item.csv', 'dtb_customer.csv']],
48+
'member_test' => ['member_test.tar.gz', ['dtb_member.csv', 'mtb_authority.csv']],
49+
];
50+
}
51+
52+
/**
53+
* @dataProvider tarGzProvider
54+
*/
55+
public function testTarGz解凍(string $filename, array $expectedFiles)
56+
{
57+
$archivePath = $this->fixturesDir . $filename;
58+
$fileNames = $this->service->extractArchive($archivePath, $this->tmpDir);
59+
60+
self::assertNotEmpty($fileNames, $filename . ' のファイル一覧が空');
61+
62+
// 期待するファイルが解凍されているか確認
63+
foreach ($expectedFiles as $expected) {
64+
$found = false;
65+
// サブディレクトリ内も検索
66+
$iterator = new \RecursiveIteratorIterator(
67+
new \RecursiveDirectoryIterator($this->tmpDir, \FilesystemIterator::SKIP_DOTS)
68+
);
69+
foreach ($iterator as $file) {
70+
if ($file->getFilename() === $expected) {
71+
$found = true;
72+
break;
73+
}
74+
}
75+
self::assertTrue($found, $filename . ' から ' . $expected . ' が解凍されていること');
76+
}
77+
}
78+
79+
public function testZip解凍()
80+
{
81+
// テスト用ZIPを作成
82+
$zipPath = $this->tmpDir . '/test.zip';
83+
$zip = new \ZipArchive();
84+
$zip->open($zipPath, \ZipArchive::CREATE);
85+
$zip->addFromString('dtb_customer.csv', "id,name\n1,test\n");
86+
$zip->addFromString('dtb_product.csv', "id,name\n1,product\n");
87+
$zip->close();
88+
89+
$outputDir = $this->tmpDir . '/output';
90+
mkdir($outputDir, 0777, true);
91+
92+
$fileNames = $this->service->extractArchive($zipPath, $outputDir);
93+
94+
self::assertContains('dtb_customer.csv', $fileNames);
95+
self::assertContains('dtb_product.csv', $fileNames);
96+
self::assertFileExists($outputDir . '/dtb_customer.csv');
97+
self::assertFileExists($outputDir . '/dtb_product.csv');
98+
}
99+
100+
public function test不正なファイルで例外()
101+
{
102+
$badFile = $this->tmpDir . '/bad.tar.gz';
103+
file_put_contents($badFile, 'not an archive');
104+
105+
$this->expectException(\RuntimeException::class);
106+
$this->service->extractArchive($badFile, $this->tmpDir);
107+
}
108+
}

composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"ec-cube/plugin-installer": "^2.0",
88
"pear/pear-core-minimal" : "^1.10.10",
99
"pear/archive_tar" : "^1.4.13",
10-
"wapmorgan/unified-archive": "^1.1.1",
1110
"nobuhiko/bulk-insert-query": "^1.1"
1211
},
1312
"extra": {

0 commit comments

Comments
 (0)