Skip to content

Commit 4e4ce28

Browse files
committed
Fix zip extract not strip dir bug
1 parent e2fd3e1 commit 4e4ce28

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

src/SPC/store/FileSystem.php

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ private static function extractArchive(string $filename, string $target): void
584584
'tar', 'xz', 'txz' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
585585
'tgz', 'gz' => f_passthru("tar -xzf {$filename} -C {$target} --strip-components 1"),
586586
'bz2' => f_passthru("tar -xjf {$filename} -C {$target} --strip-components 1"),
587-
'zip' => f_passthru("unzip {$filename} -d {$target}"),
587+
'zip' => self::unzipWithStrip($filename, $target),
588588
default => throw new FileSystemException('unknown archive format: ' . $filename),
589589
};
590590
} elseif (PHP_OS_FAMILY === 'Windows') {
@@ -599,7 +599,7 @@ private static function extractArchive(string $filename, string $target): void
599599
match (self::extname($filename)) {
600600
'tar' => f_passthru("tar -xf {$filename} -C {$target} --strip-components 1"),
601601
'xz', 'txz', 'gz', 'tgz', 'bz2' => cmd()->execWithResult("\"{$_7z}\" x -so {$filename} | tar -f - -x -C \"{$target}\" --strip-components 1"),
602-
'zip' => f_passthru("\"{$_7z}\" x {$filename} -o{$target} -y"),
602+
'zip' => self::unzipWithStrip($filename, $target),
603603
default => throw new FileSystemException("unknown archive format: {$filename}"),
604604
};
605605
}
@@ -644,4 +644,59 @@ private static function extractWithType(string $source_type, string $filename, s
644644
SPC_SOURCE_LOCAL => symlink(self::convertPath($filename), $extract_path),
645645
};
646646
}
647+
648+
/**
649+
* Unzip file with stripping top-level directory
650+
*/
651+
private static function unzipWithStrip(string $zip_file, string $extract_path): void
652+
{
653+
$temp_dir = self::convertPath(sys_get_temp_dir() . '/spc_unzip_' . bin2hex(random_bytes(16)));
654+
$zip_file = self::convertPath($zip_file);
655+
$extract_path = self::convertPath($extract_path);
656+
657+
// extract to temp dir
658+
self::createDir($temp_dir);
659+
660+
if (PHP_OS_FAMILY === 'Windows') {
661+
$mute = defined('DEBUG_MODE') ? '' : ' > NUL';
662+
// use php-sdk-binary-tools/bin/7za.exe
663+
$_7z = self::convertPath(getenv('PHP_SDK_PATH') . '/bin/7za.exe');
664+
f_passthru("\"{$_7z}\" x {$zip_file} -o{$temp_dir} -y{$mute}");
665+
} else {
666+
$mute = defined('DEBUG_MODE') ? '' : ' > /dev/null';
667+
f_passthru("unzip \"{$zip_file}\" -d \"{$temp_dir}\"{$mute}");
668+
}
669+
// scan first level dirs (relative, not recursive, include dirs)
670+
$contents = self::scanDirFiles($temp_dir, false, true, true);
671+
if ($contents === false) {
672+
throw new FileSystemException('Cannot scan unzip temp dir: ' . $temp_dir);
673+
}
674+
// if extract path already exists, remove it
675+
if (is_dir($extract_path)) {
676+
self::removeDir($extract_path);
677+
}
678+
// if only one dir, move its contents to extract_path using rename
679+
$subdir = self::convertPath("{$temp_dir}/{$contents[0]}");
680+
if (count($contents) === 1 && is_dir($subdir)) {
681+
rename($subdir, $extract_path);
682+
} else {
683+
// else, move all contents to extract_path
684+
self::createDir($extract_path);
685+
foreach ($contents as $item) {
686+
$subdir = self::convertPath("{$temp_dir}/{$item}");
687+
if (is_dir($subdir)) {
688+
// move all dir contents to extract_path (strip top-level)
689+
$sub_contents = self::scanDirFiles($subdir, false, true, true);
690+
if ($sub_contents === false) {
691+
throw new FileSystemException('Cannot scan unzip temp sub-dir: ' . $subdir);
692+
}
693+
foreach ($sub_contents as $sub_item) {
694+
rename(self::convertPath("{$subdir}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
695+
}
696+
} else {
697+
rename(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
698+
}
699+
}
700+
}
701+
}
647702
}

0 commit comments

Comments
 (0)