ncc/src/ncc/Classes/ArchiveExtractor.php
2023-09-28 21:30:00 -04:00

131 lines
No EOL
5.2 KiB
PHP

<?php
/*
* Copyright (c) Nosial 2022-2023, all rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace ncc\Classes;
use Exception;
use finfo;
use ncc\Exceptions\IOException;
use ncc\Exceptions\NotSupportedException;
use ncc\Utilities\Console;
use PharData;
use RuntimeException;
use ZipArchive;
class ArchiveExtractor
{
/**
* Extracts a given archive to a directory path
*
* @param string $archive_path
* @param string $directory_path
* @return void
* @throws IOException
* @throws NotSupportedException
*/
public static function extract(string $archive_path, string $directory_path): void
{
if(!is_file($archive_path))
{
throw new IOException(sprintf('Archive file "%s" does not exist', $archive_path));
}
$file_info = new finfo(FILEINFO_MIME);
$mime = strtolower($file_info->file($archive_path));
if(($pos = strpos($mime, ';')) !== false)
{
$mime = substr($mime, 0, $pos);
}
switch($mime)
{
case 'application/zip':
Console::outDebug(sprintf('Extracting zip archive "%s" to "%s"', $archive_path, $directory_path));
self::extractZip($archive_path, $directory_path);
break;
case 'application/x-tar':
case 'application/x-gzip':
Console::outDebug(sprintf('Extracting tar archive "%s" to "%s"', $archive_path, $directory_path));
$phar = new PharData($archive_path);
$phar->extractTo($directory_path);
break;
default:
throw new NotSupportedException(sprintf('Unable to extract archive "%s" of type "%s"', $archive_path, $file_info->file($archive_path)));
}
}
/**
* Extracts a zip archive to a directory
*
* @param string $archive_path
* @param string $directory_path
* @return void
* @throws IOException
*/
private static function extractZip(string $archive_path, string $directory_path): void
{
$zip_archive = new ZipArchive();
Console::outDebug(sprintf('Opening zip archive "%s"', $archive_path));
if(!$zip_archive->open($archive_path))
{
throw new IOException(sprintf('Unable to open zip archive "%s"', $archive_path));
}
if(!is_dir($directory_path) && !mkdir($directory_path, 0777, true) && !is_dir($directory_path))
{
throw new IOException(sprintf('Unable to create directory "%s"', $directory_path));
}
for($i=0; $i < $zip_archive->numFiles; $i++)
{
$entry_name = $zip_archive->statIndex($i)['name'];
if(str_ends_with($entry_name, '/'))
{
$concurrent_directory = $directory_path . DIRECTORY_SEPARATOR . $entry_name;
Console::outDebug(sprintf('Creating directory "%s"', $concurrent_directory));
if(!is_dir($concurrent_directory) && !mkdir($concurrent_directory, 0777, true) && !is_dir($concurrent_directory))
{
throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrent_directory));
}
continue;
}
try
{
Console::outDebug(sprintf('Extracting file "%s" from archive "%s"', $entry_name, $archive_path));
$zip_archive->extractTo($directory_path, $entry_name);
}
catch(Exception $e)
{
throw new IOException(sprintf('Unable to extract file "%s" from archive "%s"', $entry_name, $archive_path), $e);
}
}
$zip_archive->close();
}
}