Current Directory: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Archiver
Viewing File: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Archiver/Zipnative.php
<?php
/**
* Akeeba Engine
*
* @package akeebaengine
* @copyright Copyright (c)2006-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Engine\Archiver;
defined('AKEEBAENGINE') || die();
use Akeeba\Engine\Factory;
use RuntimeException;
use ZipArchive;
/**
* Class Zipnative
*
* This file uses the ZipArchive class to create and add files to existing ZIP
* archives. For more information on the use of this class, please see:
* 1. http://devzone.zend.com/article/2105 (tutorial)
* 2. http://www.php.net/manual/en/class.ziparchive.php (reference)
*
* That said, the ZipArchive class is terribly inflexible when it comes down to
* features already implemented in Akeeba Engine's Zip such as archive
* splitting, chunked processing of very large files and processing of symlinks.
* We deem it only suitable for small sites, without large files, running on a
* decent hosting facility.
*/
class Zipnative extends Base
{
/** @var string The name of the file holding the ZIP's data, which becomes the final archive */
private $_dataFileName;
/** @var ZipArchive An instance of the PHP ZIPArchive class */
private $zip = null;
/** @var int Running sum of bytes added to the archive */
private $runningSum = 0;
/** @var int Permissions for the backup archive part files */
protected $permissions = null;
/**
* Class constructor - initializes internal operating parameters
*
* @throws RuntimeException
*/
public function __construct()
{
Factory::getLog()->debug(__CLASS__ . " :: New instance");
if (!class_exists('\ZipArchive'))
{
throw new RuntimeException('Your server does not support the ZipArchive extension. Please use a different Archiver Engine and retry backing up your site');
}
parent::__construct();
}
/**
* Common code which gets called on instance creation or wake-up (unserialization)
*
* @codeCoverageIgnore
*
* @return void
*/
public function __bootstrap_code()
{
parent::__bootstrap_code();
// So that the first run doesn't crash!
if (empty($this->_dataFileName))
{
return;
}
// Try to reopen the ZIP
$this->zip = new ZipArchive;
if (!file_exists($this->_dataFileName))
{
$res = $this->zip->open($this->_dataFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
}
else
{
$res = $this->zip->open($this->_dataFileName);
}
if ($res !== true)
{
switch ($res)
{
case ZipArchive::ER_EXISTS:
throw new RuntimeException("The archive {$this->_dataFileName} already exists");
break;
case ZipArchive::ER_INCONS:
throw new RuntimeException("Inconsistent archive {$this->_dataFileName} detected");
break;
case ZipArchive::ER_INVAL:
throw new RuntimeException("Invalid archive {$this->_dataFileName} detected");
break;
case ZipArchive::ER_MEMORY:
throw new RuntimeException("Not enough memory to process archive {$this->_dataFileName}");
break;
case ZipArchive::ER_NOENT:
throw new RuntimeException("Unexpected ZipArchive::ER_NOENT error processing archive {$this->_dataFileName}");
break;
case ZipArchive::ER_NOZIP:
throw new RuntimeException("File {$this->_dataFileName} is not a ZIP archive!");
break;
case ZipArchive::ER_OPEN:
throw new RuntimeException("Could not open archive file {$this->_dataFileName} for writing");
break;
case ZipArchive::ER_READ:
throw new RuntimeException("Could not read from archive file {$this->_dataFileName}");
break;
case ZipArchive::ER_SEEK:
throw new RuntimeException("Could not seek into position while processing archive file {$this->_dataFileName}");
break;
}
}
}
/**
* Initialises the archiver class, creating the archive from an existent
* installer's JPA archive.
*
* @param string $targetArchivePath Absolute path to the generated archive
* @param array $options A named key array of options (optional). This is currently not supported
*
* @return void
*/
public function initialize($targetArchivePath, $options = [])
{
Factory::getLog()->debug(__CLASS__ . " :: initialize - archive $targetArchivePath");
// Get names of temporary files
$this->_dataFileName = $targetArchivePath;
// Try to kill the archive if it exists
Factory::getLog()->debug(__CLASS__ . " :: Killing old archive");
$fp = fopen($this->_dataFileName, "w");
if (!($fp === false))
{
ftruncate($fp, 0);
$this->conditionalFileClose($fp);
}
else
{
@unlink($this->_dataFileName);
}
$this->runningSum = 0;
// Make sure we open the file
$this->__bootstrap_code();
}
/**
* In this engine, we have no finalization, really
*
* @return void
*/
public function finalize()
{
$this->zip->close();
@chmod($this->_dataFileName, $this->getPermissions());
}
/**
* Returns a string with the extension (including the dot) of the files produced
* by this class.
*
* @return string
*/
public function getExtension()
{
return '.zip';
}
/**
* The most basic file transaction: add a single entry (file or directory) to
* the archive.
*
* @param bool $isVirtual If true, the next parameter contains file data instead of a file name
* @param string $sourceNameOrData Absolute file name to read data from or the file data itself is $isVirtual is
* true
* @param string $targetName The (relative) file name under which to store the file in the archive
*
* @return bool True on success, false otherwise
*/
protected function _addFile($isVirtual, &$sourceNameOrData, $targetName)
{
if (!is_object($this->zip))
{
return false;
}
if (!$isVirtual)
{
Factory::getLog()->debug(__CLASS__ . " :: Adding $sourceNameOrData");
if (is_dir($sourceNameOrData))
{
$result = $this->zip->addEmptyDir($targetName);
}
else
{
$this->runningSum += filesize($sourceNameOrData);
$result = $this->zip->addFile($sourceNameOrData, $targetName);
}
}
else
{
Factory::getLog()->debug(' Virtual add:' . $targetName . ' (' . strlen($sourceNameOrData) . ')');
$this->runningSum += strlen($sourceNameOrData);
if (empty($sourceNameOrData))
{
$result = $this->zip->addEmptyDir($targetName);
}
else
{
$result = $this->zip->addFromString($targetName, $sourceNameOrData);
}
}
$this->zip->close();
$this->__bootstrap_code();
return true;
}
/**
* Return the requested permissions for the backup archive file.
*
* @return int
* @since 8.0.0
*/
protected function getPermissions(): int
{
if (!is_null($this->permissions))
{
return $this->permissions;
}
$configuration = Factory::getConfiguration();
$permissions = $configuration->get('engine.archiver.common.permissions', '0666') ?: '0666';
$this->permissions = octdec($permissions);
return $this->permissions;
}
}