commit vendor
This commit is contained in:
649
vendor/wapmorgan/unified-archive/src/Formats/Tar.php
vendored
Normal file
649
vendor/wapmorgan/unified-archive/src/Formats/Tar.php
vendored
Normal file
@ -0,0 +1,649 @@
|
||||
<?php
|
||||
namespace wapmorgan\UnifiedArchive\Formats;
|
||||
|
||||
use Archive_Tar;
|
||||
use Exception;
|
||||
use FilesystemIterator;
|
||||
use Phar;
|
||||
use PharData;
|
||||
use RecursiveIteratorIterator;
|
||||
use wapmorgan\UnifiedArchive\ArchiveEntry;
|
||||
use wapmorgan\UnifiedArchive\ArchiveInformation;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\ArchiveCreationException;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\ArchiveModificationException;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\NonExistentArchiveFileException;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedArchiveException;
|
||||
use wapmorgan\UnifiedArchive\LzwStreamWrapper;
|
||||
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* Tar format handler
|
||||
* @package wapmorgan\UnifiedArchive\Formats
|
||||
*/
|
||||
class Tar extends BasicFormat
|
||||
{
|
||||
const TAR = 'tar';
|
||||
const TAR_GZIP = 'tgz';
|
||||
const TAR_BZIP = 'tbz2';
|
||||
const TAR_LZMA = 'txz';
|
||||
const TAR_LZW = 'tar.z';
|
||||
|
||||
/** @var bool */
|
||||
static protected $enabledPearTar;
|
||||
|
||||
/** @var bool */
|
||||
static protected $enabledPharData;
|
||||
/**
|
||||
* Checks system configuration for available Tar-manipulation libraries
|
||||
*/
|
||||
protected static function checkRequirements()
|
||||
{
|
||||
if (self::$enabledPharData === null || self::$enabledPearTar === null) {
|
||||
self::$enabledPearTar = class_exists('\Archive_Tar');
|
||||
self::$enabledPharData = class_exists('\PharData');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether archive can be opened with current system configuration
|
||||
* @param $archiveFileName
|
||||
* @return boolean
|
||||
*/
|
||||
// public static function canOpenArchive($archiveFileName)
|
||||
// {
|
||||
// self::checkRequirements();
|
||||
//
|
||||
// $type = self::detectArchiveType($archiveFileName);
|
||||
// if ($type !== false) {
|
||||
// return self::canOpenType($type);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Detect archive type by its filename or content.
|
||||
* @param $archiveFileName
|
||||
* @param bool $contentCheck
|
||||
* @return string|boolean One of TarArchive type constants OR false if type is not detected
|
||||
*/
|
||||
public static function detectArchiveType($archiveFileName, $contentCheck = true)
|
||||
{
|
||||
// by file name
|
||||
if (preg_match('~\.(?<ext>tar|tgz|tbz2|txz|tar\.(gz|bz2|xz|z))$~', strtolower($archiveFileName), $match)) {
|
||||
switch ($match['ext']) {
|
||||
case 'tar':
|
||||
return self::TAR;
|
||||
|
||||
case 'tgz':
|
||||
case 'tar.gz':
|
||||
return self::TAR_GZIP;
|
||||
|
||||
case 'tbz2':
|
||||
case 'tar.bz2':
|
||||
return self::TAR_BZIP;
|
||||
|
||||
case 'txz':
|
||||
case 'tar.xz':
|
||||
return self::TAR_LZMA;
|
||||
|
||||
case 'tar.z':
|
||||
return self::TAR_LZW;
|
||||
}
|
||||
}
|
||||
|
||||
// by content
|
||||
if ($contentCheck) {
|
||||
$mime_type = mime_content_type($archiveFileName);
|
||||
switch ($mime_type) {
|
||||
case 'application/x-tar':
|
||||
return self::TAR;
|
||||
case 'application/x-gtar':
|
||||
return self::TAR_GZIP;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether specific archive type can be opened with current system configuration
|
||||
* @param $type
|
||||
* @return boolean
|
||||
*/
|
||||
// public static function canOpenType($type)
|
||||
// {
|
||||
// self::checkRequirements();
|
||||
// switch ($type) {
|
||||
// case self::TAR:
|
||||
// return self::$enabledPearTar || self::$enabledPharData;
|
||||
//
|
||||
// case self::TAR_GZIP:
|
||||
// return (self::$enabledPearTar || self::$enabledPharData) && extension_loaded('zlib');
|
||||
//
|
||||
// case self::TAR_BZIP:
|
||||
// return (self::$enabledPearTar || self::$enabledPharData) && extension_loaded('bz2');
|
||||
//
|
||||
//
|
||||
// case self::TAR_LZMA:
|
||||
// return self::$enabledPearTar && extension_loaded('lzma2');
|
||||
//
|
||||
// case self::TAR_LZW:
|
||||
// return self::$enabledPearTar && LzwStreamWrapper::isBinaryAvailable();
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
* @param string $archiveFileName
|
||||
* @return false|int
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function createArchive(array $files, $archiveFileName)
|
||||
{
|
||||
static::checkRequirements();
|
||||
|
||||
if (static::$enabledPharData)
|
||||
return static::createArchiveForPhar($files, $archiveFileName);
|
||||
|
||||
if (static::$enabledPearTar)
|
||||
return static::createArchiveForPear($files, $archiveFileName);
|
||||
|
||||
throw new UnsupportedOperationException('Archive_Tar nor PharData not available');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an archive via Pear library
|
||||
* @param array $files
|
||||
* @param $archiveFileName
|
||||
* @return int
|
||||
* @throws ArchiveCreationException
|
||||
*/
|
||||
protected static function createArchiveForPear(array $files, $archiveFileName)
|
||||
{
|
||||
$compression = null;
|
||||
switch (strtolower(pathinfo($archiveFileName, PATHINFO_EXTENSION))) {
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
$compression = 'gz';
|
||||
break;
|
||||
case 'bz2':
|
||||
case 'tbz2':
|
||||
$compression = 'bz2';
|
||||
break;
|
||||
case 'xz':
|
||||
$compression = 'lzma2';
|
||||
break;
|
||||
case 'z':
|
||||
$tar_aname = 'compress.lzw://' . $archiveFileName;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($tar_aname))
|
||||
$tar = new Archive_Tar($tar_aname, $compression);
|
||||
else
|
||||
$tar = new Archive_Tar($archiveFileName, $compression);
|
||||
|
||||
foreach ($files as $localName => $filename) {
|
||||
$remove_dir = dirname($filename);
|
||||
$add_dir = dirname($localName);
|
||||
|
||||
if (is_null($filename)) {
|
||||
if ($tar->addString($localName, '') === false)
|
||||
throw new ArchiveCreationException('Error when adding directory '.$localName.' to archive');
|
||||
} else {
|
||||
if ($tar->addModify($filename, $add_dir, $remove_dir) === false)
|
||||
throw new ArchiveCreationException('Error when adding file '.$filename.' to archive');
|
||||
}
|
||||
}
|
||||
$tar = null;
|
||||
|
||||
return count($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an archive via Phar library
|
||||
* @param array $files
|
||||
* @param $archiveFileName
|
||||
* @return bool
|
||||
* @throws ArchiveCreationException
|
||||
*/
|
||||
protected static function createArchiveForPhar(array $files, $archiveFileName)
|
||||
{
|
||||
if (preg_match('~^(.+)\.(tar\.(gz|bz2))$~i', $archiveFileName, $match)) {
|
||||
$ext = $match[2];
|
||||
$basename = $match[1];
|
||||
} else {
|
||||
$ext = pathinfo($archiveFileName, PATHINFO_EXTENSION);
|
||||
$basename = dirname($archiveFileName).'/'.basename($archiveFileName, '.'.$ext);
|
||||
}
|
||||
$tar = new PharData($basename.'.tar', 0, null, Phar::TAR);
|
||||
|
||||
try {
|
||||
foreach ($files as $localName => $filename) {
|
||||
if (is_null($filename)) {
|
||||
if (!in_array($localName, ['/', ''], true)) {
|
||||
if ($tar->addEmptyDir($localName) === false) {
|
||||
throw new ArchiveCreationException('Error when adding directory '.$localName.' to archive');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($tar->addFile($filename, $localName) === false) {
|
||||
throw new ArchiveCreationException('Error when adding file '.$localName.' to archive');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new ArchiveCreationException('Error when creating archive: '.$e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
switch (strtolower(pathinfo($archiveFileName, PATHINFO_EXTENSION))) {
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
$tar->compress(Phar::GZ, $ext);
|
||||
break;
|
||||
case 'bz2':
|
||||
case 'tbz2':
|
||||
$tar->compress(Phar::BZ2, $ext);
|
||||
break;
|
||||
}
|
||||
$tar = null;
|
||||
|
||||
return count($files);
|
||||
}
|
||||
|
||||
/** @var string Full path to archive */
|
||||
protected $archiveFileName;
|
||||
|
||||
/** @var string Full path to archive */
|
||||
protected $archiveType;
|
||||
|
||||
/** @var Archive_Tar|PharData */
|
||||
protected $tar;
|
||||
|
||||
/** @var float Overall compression ratio of Tar archive when Archive_Tar is used */
|
||||
protected $pearCompressionRatio;
|
||||
|
||||
/** @var array<string, integer> List of files and their index in listContent() result */
|
||||
protected $pearFilesIndex;
|
||||
|
||||
/** @var int Flags for iterator */
|
||||
const PHAR_FLAGS = FilesystemIterator::UNIX_PATHS;
|
||||
|
||||
/**
|
||||
* Tar format constructor.
|
||||
*
|
||||
* @param string $archiveFileName
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($archiveFileName)
|
||||
{
|
||||
static::checkRequirements();
|
||||
|
||||
$this->archiveFileName = realpath($archiveFileName);
|
||||
$this->archiveType = static::detectArchiveType($this->archiveFileName);
|
||||
|
||||
if ($this->archiveType === false)
|
||||
throw new UnsupportedArchiveException('Could not detect type for archive '.$this->archiveFileName);
|
||||
|
||||
$this->open($this->archiveType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tar destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->tar = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $archiveType
|
||||
* @throws UnsupportedArchiveException
|
||||
*/
|
||||
protected function open($archiveType)
|
||||
{
|
||||
switch ($archiveType) {
|
||||
case self::TAR_GZIP:
|
||||
if (self::$enabledPharData) {
|
||||
$this->tar = new PharData($this->archiveFileName, self::PHAR_FLAGS);
|
||||
} else {
|
||||
$this->tar = new Archive_Tar($this->archiveFileName, 'gz');
|
||||
}
|
||||
break;
|
||||
|
||||
case self::TAR_BZIP:
|
||||
if (self::$enabledPharData) {
|
||||
$this->tar = new PharData($this->archiveFileName, self::PHAR_FLAGS);
|
||||
} else {
|
||||
$this->tar = new Archive_Tar($this->archiveFileName, 'bz2');
|
||||
}
|
||||
break;
|
||||
|
||||
case self::TAR_LZMA:
|
||||
if (!self::$enabledPearTar) {
|
||||
throw new UnsupportedArchiveException('Archive_Tar not available');
|
||||
}
|
||||
$this->tar = new Archive_Tar($this->archiveFileName, 'lzma2');
|
||||
break;
|
||||
|
||||
case self::TAR_LZW:
|
||||
if (!self::$enabledPearTar) {
|
||||
throw new UnsupportedArchiveException('Archive_Tar not available');
|
||||
}
|
||||
|
||||
LzwStreamWrapper::registerWrapper();
|
||||
$this->tar = new Archive_Tar('compress.lzw://' . $this->archiveFileName);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (self::$enabledPharData) {
|
||||
$this->tar = new PharData($this->archiveFileName, self::PHAR_FLAGS);
|
||||
} else {
|
||||
$this->tar = new Archive_Tar($this->archiveFileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArchiveInformation
|
||||
*/
|
||||
public function getArchiveInformation()
|
||||
{
|
||||
$information = new ArchiveInformation();
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
$this->pearFilesIndex = [];
|
||||
|
||||
foreach ($this->tar->listContent() as $i => $file) {
|
||||
// BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
|
||||
if ($file['filename'] === 'pax_global_header') {
|
||||
continue;
|
||||
}
|
||||
$information->files[] = $file['filename'];
|
||||
$information->uncompressedFilesSize += $file['size'];
|
||||
$this->pearFilesIndex[$file['filename']] = $i;
|
||||
}
|
||||
|
||||
$information->uncompressedFilesSize = filesize($this->archiveFileName);
|
||||
$this->pearCompressionRatio = $information->uncompressedFilesSize != 0
|
||||
? ceil($information->compressedFilesSize / $information->uncompressedFilesSize)
|
||||
: 1;
|
||||
} else {
|
||||
$stream_path_length = strlen('phar://'.$this->archiveFileName.'/');
|
||||
foreach (new RecursiveIteratorIterator($this->tar) as $i => $file) {
|
||||
$information->files[] = substr($file->getPathname(), $stream_path_length);
|
||||
$information->compressedFilesSize += $file->getCompressedSize();
|
||||
$information->uncompressedFilesSize += filesize($file->getPathname());
|
||||
}
|
||||
}
|
||||
|
||||
return $information;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFileNames()
|
||||
{
|
||||
return $this->tar instanceof Archive_Tar
|
||||
? $this->getFileNamesForPear()
|
||||
: $this->getFileNamesForPhar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return bool
|
||||
*/
|
||||
public function isFileExists($fileName)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar)
|
||||
return isset($this->pearFilesIndex[$fileName]);
|
||||
|
||||
try {
|
||||
$this->tar->offsetGet($fileName);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return ArchiveEntry
|
||||
* @throws NonExistentArchiveFileException
|
||||
*/
|
||||
public function getFileData($fileName)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
if (!isset($this->pearFilesIndex[$fileName]))
|
||||
throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
|
||||
|
||||
$index = $this->pearFilesIndex[$fileName];
|
||||
|
||||
$files_list = $this->tar->listContent();
|
||||
if (!isset($files_list[$index]))
|
||||
throw new NonExistentArchiveFileException('File '.$fileName.' is not found in Tar archive');
|
||||
|
||||
$data = $files_list[$index];
|
||||
unset($files_list);
|
||||
|
||||
return new ArchiveEntry($fileName, $data['size'] / $this->pearCompressionRatio,
|
||||
$data['size'], $data['mtime'], in_array(strtolower(pathinfo($this->archiveFileName,
|
||||
PATHINFO_EXTENSION)), array('gz', 'bz2', 'xz', 'Z')));
|
||||
}
|
||||
|
||||
/** @var \PharFileInfo $entry_info */
|
||||
$entry_info = $this->tar->offsetGet($fileName);
|
||||
return new ArchiveEntry($fileName, $entry_info->getSize(), filesize($entry_info->getPathname()),
|
||||
0, $entry_info->isCompressed());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return string
|
||||
* @throws NonExistentArchiveFileException
|
||||
*/
|
||||
public function getFileContent($fileName)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
if (!isset($this->pearFilesIndex[$fileName]))
|
||||
throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
|
||||
|
||||
return $this->tar->extractInString($fileName);
|
||||
}
|
||||
|
||||
return $this->tar->offsetGet($fileName)->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return resource
|
||||
* @throws NonExistentArchiveFileException
|
||||
*/
|
||||
public function getFileResource($fileName)
|
||||
{
|
||||
$resource = fopen('php://temp', 'r+');
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
if (!isset($this->pearFilesIndex[$fileName]))
|
||||
throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
|
||||
|
||||
fwrite($resource, $this->tar->extractInString($fileName));
|
||||
} else
|
||||
fwrite($resource, $this->tar->offsetGet($fileName)->getContent());
|
||||
|
||||
rewind($resource);
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $outputFolder
|
||||
* @param array $files
|
||||
* @return int
|
||||
* @throws ArchiveExtractionException
|
||||
*/
|
||||
public function extractFiles($outputFolder, array $files)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
$result = $this->tar->extractList($files, $outputFolder);
|
||||
} else {
|
||||
$result = $this->tar->extractTo($outputFolder, $files, true);
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
|
||||
}
|
||||
|
||||
return count($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $outputFolder
|
||||
* @return false|int
|
||||
* @throws ArchiveExtractionException
|
||||
*/
|
||||
public function extractArchive($outputFolder)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
$result = $this->tar->extract($outputFolder);
|
||||
} else {
|
||||
$result = $this->tar->extractTo($outputFolder, null, true);
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
* @return int
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public function deleteFiles(array $files)
|
||||
{
|
||||
if ($this->tar instanceof Archive_Tar)
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
$deleted = 0;
|
||||
|
||||
foreach ($files as $i => $file) {
|
||||
if ($this->tar->delete($file))
|
||||
$deleted++;
|
||||
}
|
||||
|
||||
$this->tar = null;
|
||||
$this->open($this->archiveType);
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
* @return false|int
|
||||
* @throws ArchiveModificationException
|
||||
*/
|
||||
public function addFiles(array $files)
|
||||
{
|
||||
$added = 0;
|
||||
|
||||
if ($this->tar instanceof Archive_Tar) {
|
||||
foreach ($files as $localName => $filename) {
|
||||
$remove_dir = dirname($filename);
|
||||
$add_dir = dirname($localName);
|
||||
if (is_null($filename)) {
|
||||
if ($this->tar->addString($localName, "") === false) {
|
||||
throw new ArchiveModificationException('Could not add directory "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
|
||||
}
|
||||
} else {
|
||||
if ($this->tar->addModify($filename, $add_dir, $remove_dir) === false) {
|
||||
throw new ArchiveModificationException('Could not add file "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
|
||||
}
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
foreach ($files as $localName => $filename) {
|
||||
if (is_null($filename)) {
|
||||
$this->tar->addEmptyDir($localName);
|
||||
} else {
|
||||
$this->tar->addFile($filename, $localName);
|
||||
$added++;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new ArchiveModificationException('Could not add file "'.$filename.'": '.$e->getMessage(), $e->getCode());
|
||||
}
|
||||
$this->tar = null;
|
||||
// reopen to refresh files list properly
|
||||
$this->open($this->archiveType);
|
||||
}
|
||||
|
||||
return $added;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileNamesForPear()
|
||||
{
|
||||
$files = [];
|
||||
|
||||
$Content = $this->tar->listContent();
|
||||
foreach ($Content as $i => $file) {
|
||||
// BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
|
||||
if ($file['filename'] === 'pax_global_header') {
|
||||
continue;
|
||||
}
|
||||
$files[] = $file['filename'];
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileNamesForPhar()
|
||||
{
|
||||
$files = [];
|
||||
|
||||
$stream_path_length = strlen('phar://'.$this->archiveFileName.'/');
|
||||
foreach (new RecursiveIteratorIterator($this->tar) as $i => $file) {
|
||||
$files[] = substr($file->getPathname(), $stream_path_length);
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function canCreateArchive()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function canAddFiles()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function canDeleteFiles()
|
||||
{
|
||||
static::checkRequirements();
|
||||
return self::$enabledPharData;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user