first commit
This commit is contained in:
931
administrator/components/com_akeebabackup/engine/Factory.php
Normal file
931
administrator/components/com_akeebabackup/engine/Factory.php
Normal file
@@ -0,0 +1,931 @@
|
||||
<?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;
|
||||
|
||||
defined('AKEEBAENGINE') || die();
|
||||
|
||||
use Akeeba\Engine\Base\Part;
|
||||
use Akeeba\Engine\Core\Database;
|
||||
use Akeeba\Engine\Core\Filters;
|
||||
use Akeeba\Engine\Core\Kettenrad;
|
||||
use Akeeba\Engine\Core\Timer;
|
||||
use Akeeba\Engine\Driver\Base;
|
||||
use Akeeba\Engine\Postproc\PostProcInterface;
|
||||
use Akeeba\Engine\Util\ConfigurationCheck;
|
||||
use Akeeba\Engine\Util\Encrypt;
|
||||
use Akeeba\Engine\Util\EngineParameters;
|
||||
use Akeeba\Engine\Util\FactoryStorage;
|
||||
use Akeeba\Engine\Util\FileLister;
|
||||
use Akeeba\Engine\Util\FileSystem;
|
||||
use Akeeba\Engine\Util\Logger;
|
||||
use Akeeba\Engine\Util\PushMessages;
|
||||
use Akeeba\Engine\Util\RandomValue;
|
||||
use Akeeba\Engine\Util\SecureSettings;
|
||||
use Akeeba\Engine\Util\Statistics;
|
||||
use Akeeba\Engine\Util\TemporaryFiles;
|
||||
use Exception;
|
||||
use RuntimeException;
|
||||
|
||||
// Try to kill errors display
|
||||
if (function_exists('ini_set') && !defined('AKEEBADEBUG'))
|
||||
{
|
||||
ini_set('display_errors', false);
|
||||
}
|
||||
|
||||
// Make sure the class autoloader is loaded
|
||||
require_once __DIR__ . '/Autoloader.php';
|
||||
|
||||
/**
|
||||
* The Akeeba Engine Factory class
|
||||
*
|
||||
* This class is responsible for instantiating all Akeeba Engine classes
|
||||
*/
|
||||
abstract class Factory
|
||||
{
|
||||
/**
|
||||
* The absolute path to Akeeba Engine's installation
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $root;
|
||||
|
||||
/**
|
||||
* Partial class names of the loaded engines e.g. 'archiver' => 'Archiver\\Jpa'. Survives serialization.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $engineClassnames = [];
|
||||
|
||||
/**
|
||||
* A list of instantiated objects which will persist after serialisation / unserialisation
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $objectList = [];
|
||||
|
||||
/**
|
||||
* A list of instantiated objects which will NOT persist after serialisation / unserialisation
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $temporaryObjectList = [];
|
||||
|
||||
/**
|
||||
* The class to use for push messages
|
||||
*
|
||||
* @since 9.3.1
|
||||
* @var string
|
||||
*/
|
||||
private static $pushClassName = 'Util\\PushMessages';
|
||||
|
||||
/**
|
||||
* Gets a serialized snapshot of the Factory for safekeeping (hibernate)
|
||||
*
|
||||
* @return string The serialized snapshot of the Factory
|
||||
*/
|
||||
public static function serialize()
|
||||
{
|
||||
// Call _onSerialize in all objects known to the factory
|
||||
foreach (static::$objectList as $class_name => $object)
|
||||
{
|
||||
if (method_exists($object, '_onSerialize'))
|
||||
{
|
||||
call_user_func([$object, '_onSerialize']);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialise an array with all the engine information
|
||||
$engineInfo = [
|
||||
'root' => static::$root,
|
||||
'objectList' => static::$objectList,
|
||||
'engineClassnames' => static::$engineClassnames,
|
||||
'pushClassname' => static::$pushClassName,
|
||||
];
|
||||
|
||||
// Serialize the factory
|
||||
return serialize($engineInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates the full Factory state from a serialized snapshot (resume)
|
||||
*
|
||||
* @param string $serialized_data The serialized snapshot to resume from
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function unserialize($serialized_data)
|
||||
{
|
||||
static::nuke();
|
||||
|
||||
$engineInfo = unserialize($serialized_data);
|
||||
|
||||
static::$root = $engineInfo['root'] ?? '';
|
||||
static::$objectList = $engineInfo['objectList'] ?? [];
|
||||
static::$engineClassnames = $engineInfo['engineClassnames'] ?? [];
|
||||
static::$pushClassName = $engineInfo['pushClassname'] ?? 'Utils\\PushMessages';
|
||||
static::$temporaryObjectList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the internal factory state, freeing all previously created objects
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function nuke()
|
||||
{
|
||||
foreach (static::$objectList as $key => $object)
|
||||
{
|
||||
$object = null;
|
||||
}
|
||||
|
||||
foreach (static::$temporaryObjectList as $key => $object)
|
||||
{
|
||||
$object = null;
|
||||
}
|
||||
|
||||
static::$objectList = [];
|
||||
static::$temporaryObjectList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the engine state to temporary storage
|
||||
*
|
||||
* @param string $tag The backup origin to save. Leave empty to get from already loaded Kettenrad instance.
|
||||
* @param string $backupId The backup ID to save. Leave empty to get from already loaded Kettenrad instance.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException When the state save fails for any reason
|
||||
*/
|
||||
public static function saveState($tag = null, $backupId = null)
|
||||
{
|
||||
$kettenrad = static::getKettenrad();
|
||||
$tag = $tag ?: $kettenrad->getTag();
|
||||
$backupId = $backupId ?: $kettenrad->getBackupId();
|
||||
|
||||
$saveTag = rtrim($tag . '.' . ($backupId ?: ''), '.');
|
||||
$ret = $kettenrad->getStatusArray();
|
||||
|
||||
if ($ret['HasRun'] == 1)
|
||||
{
|
||||
Factory::getLog()->debug("Will not save a finished Kettenrad instance");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Factory::getLog()->debug("Saving Kettenrad instance $tag");
|
||||
|
||||
// Save a Factory snapshot
|
||||
$factoryStorage = static::getFactoryStorage();
|
||||
|
||||
$logger = static::getLog();
|
||||
$logger->resetWarnings();
|
||||
|
||||
$serializedFactoryData = static::serialize();
|
||||
$memoryFileExtension = 'php';
|
||||
$result = $factoryStorage->set($serializedFactoryData, $saveTag, $memoryFileExtension);
|
||||
|
||||
/**
|
||||
* Some hosts, such as WPEngine, do not allow us to save the memory files in .php files. In this case we use the
|
||||
* far more insecure .dat extension.
|
||||
*/
|
||||
if ($result === false)
|
||||
{
|
||||
$memoryFileExtension = 'dat';
|
||||
$result = $factoryStorage->set($serializedFactoryData, $saveTag, $memoryFileExtension);
|
||||
}
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
$saveKey = $factoryStorage->get_storage_filename($saveTag, $memoryFileExtension);
|
||||
$errorMessage = "Cannot save factory state in storage, storage filename $saveKey";
|
||||
|
||||
$logger->error($errorMessage);
|
||||
|
||||
throw new RuntimeException($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the engine state from the storage (if it exists).
|
||||
*
|
||||
* When failIfMissing is true (default) an exception will be thrown if the memory file / database record is no
|
||||
* longer there. This is a clear indication of an issue with the storage engine, e.g. the host deleting the memory
|
||||
* files in the middle of the backup step. Therefore we'll switch the storage engine type before throwing the
|
||||
* exception.
|
||||
*
|
||||
* When failIfMissing is false we do NOT throw an exception. Instead, we do a hard reset of the backup factory. This
|
||||
* is required by the resetState method when we ask it to reset multiple origins at once.
|
||||
*
|
||||
* @param string $tag The backup origin to load
|
||||
* @param string $backupId The backup ID to load
|
||||
* @param bool $failIfMissing Throw an exception if the memory data is no longer there
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function loadState($tag = null, $backupId = null, $failIfMissing = true)
|
||||
{
|
||||
$tag = $tag ?: (defined('AKEEBA_BACKUP_ORIGIN') ? AKEEBA_BACKUP_ORIGIN : 'backend');
|
||||
$loadTag = rtrim($tag . '.' . ($backupId ?: ''), '.');
|
||||
|
||||
// In order to load anything, we need to have the correct profile loaded. Let's assume
|
||||
// that the latest backup record in this tag has the correct profile number set.
|
||||
$config = static::getConfiguration();
|
||||
|
||||
if (empty($config->activeProfile))
|
||||
{
|
||||
$profile = Platform::getInstance()->get_active_profile();
|
||||
|
||||
if (empty($profile) || ($profile <= 1))
|
||||
{
|
||||
// Only bother loading a configuration if none has been already loaded
|
||||
$filters = [
|
||||
['field' => 'tag', 'value' => $tag],
|
||||
];
|
||||
|
||||
if (!empty($backupId))
|
||||
{
|
||||
$filters[] = ['field' => 'backupid', 'value' => $backupId];
|
||||
}
|
||||
|
||||
$statList = Platform::getInstance()->get_statistics_list([
|
||||
'filters' => $filters, 'order' => [
|
||||
'by' => 'id', 'order' => 'DESC',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (is_array($statList))
|
||||
{
|
||||
$stat = array_pop($statList) ?? [];
|
||||
$profile = $stat['profile_id'] ?? 1;
|
||||
}
|
||||
}
|
||||
|
||||
Platform::getInstance()->load_configuration($profile);
|
||||
}
|
||||
|
||||
$profile = $config->activeProfile;
|
||||
|
||||
Factory::getLog()->open($loadTag);
|
||||
Factory::getLog()->debug("Kettenrad :: Attempting to load from database ($tag) [$loadTag]");
|
||||
|
||||
$serialized_factory = static::getFactoryStorage()->get($loadTag);
|
||||
|
||||
if ($serialized_factory === false)
|
||||
{
|
||||
if ($failIfMissing)
|
||||
{
|
||||
throw new RuntimeException("Akeeba Engine detected a problem while saving temporary data. Please restart your backup.", 500);
|
||||
}
|
||||
|
||||
// There is no serialized factory. Nuke the in-memory factory.
|
||||
Factory::getLog()->debug(" -- Stored Akeeba Factory ($tag) [$loadTag] not found - hard reset");
|
||||
static::nuke();
|
||||
Platform::getInstance()->load_configuration($profile);
|
||||
}
|
||||
|
||||
Factory::getLog()->debug(" -- Loaded stored Akeeba Factory ($tag) [$loadTag]");
|
||||
static::unserialize($serialized_factory);
|
||||
|
||||
unset($serialized_factory);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Public factory interface
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Resets the engine state, wiping out any pending backups and/or stale temporary data.
|
||||
*
|
||||
* The configuration parameters are:
|
||||
*
|
||||
* * global `bool` True to reset all backups, regardless of the origin or profile ID
|
||||
* * log `bool` True to log our actions (default: false)
|
||||
* * maxrun `int` Only backup records older than this number of seconds will be reset (default: 180)
|
||||
*
|
||||
* Special considerations:
|
||||
*
|
||||
* * If global = true all backups from all origins are taken into account to determine which ones are stuck (over
|
||||
* the maxrun threshold since their last database entry).
|
||||
*
|
||||
* * If global = false only backups from the current backup origin are taken into account.
|
||||
*
|
||||
* * If global = false AND the current origin is 'backend' all pending and idle backups with the 'backup' origin are
|
||||
* considered stuck regardless of their age. In other words, maxrun is effectively set to 0. The idea is that only
|
||||
* a single person, from a single browser, should be taking backend backups at a time. Resetting single origin
|
||||
* backups is only ever meant to be called by the consumer when starting a backup.
|
||||
*
|
||||
* * Corollary to the above: starting a frontend, CLI or JSON API backup with the same backup profile DOES NOT reset
|
||||
* a previously failed backup if the new backup starts less than 'maxrun' seconds since the last step of the
|
||||
* failed backup started.
|
||||
*
|
||||
* * The time information for the backup age is taken from the database, namely the backupend field. If no time
|
||||
* is recorded for the last step we use the backupstart field instead.
|
||||
*
|
||||
* @param array $config Configuration parameters for the reset operation
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function resetState($config = [])
|
||||
{
|
||||
$default_config = [
|
||||
'global' => true,
|
||||
'log' => false,
|
||||
'maxrun' => 180,
|
||||
];
|
||||
|
||||
$config = (object) array_merge($default_config, $config);
|
||||
|
||||
// Pause logging if so desired
|
||||
if (!$config->log)
|
||||
{
|
||||
Factory::getLog()->pause();
|
||||
}
|
||||
|
||||
// Get the origin to clear, depending on the 'global' setting
|
||||
$originTag = $config->global ? null : Platform::getInstance()->get_backup_origin();
|
||||
|
||||
// Cache the factory before proceeding
|
||||
$factory = static::serialize();
|
||||
|
||||
// Get all running backups for the selected origin (or all origins, if global was false).
|
||||
$runningList = Platform::getInstance()->get_running_backups($originTag);
|
||||
|
||||
// Sanity check
|
||||
if (!is_array($runningList))
|
||||
{
|
||||
$runningList = [];
|
||||
}
|
||||
|
||||
// If the current origin is 'backend' we assume maxrun = 0 per the method docblock notes.
|
||||
$maxRun = ($originTag == 'backend') ? 0 : $config->maxrun;
|
||||
|
||||
// Filter out entries by backup age
|
||||
$now = time();
|
||||
$cutOff = $now - $maxRun;
|
||||
$runningList = array_filter($runningList, function (array $running) use ($cutOff, $maxRun) {
|
||||
// No cutoff time: include all currently running backup records
|
||||
if ($maxRun == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to get the last backup tick timestamp
|
||||
try
|
||||
{
|
||||
$backupTickTime = !empty($running['backupend']) ? $running['backupend'] : $running['backupstart'];
|
||||
$tz = new \DateTimeZone('UTC');
|
||||
$tstamp = (new \DateTime($backupTickTime, $tz))->getTimestamp();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$tstamp = Factory::getLog()->getLastTimestamp($running['origin']);
|
||||
}
|
||||
|
||||
if (is_null($tstamp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only include still running backups whose last tick was BEFORE the cutoff time
|
||||
return $tstamp <= $cutOff;
|
||||
});
|
||||
|
||||
// Mark running backups as failed
|
||||
foreach ($runningList as $running)
|
||||
{
|
||||
// Delete the failed backup's leftover archive parts
|
||||
$filenames = Factory::getStatistics()->get_all_filenames($running, false);
|
||||
$filenames = is_null($filenames) ? [] : $filenames;
|
||||
$totalSize = 0;
|
||||
|
||||
foreach ($filenames as $failedArchive)
|
||||
{
|
||||
if (!@file_exists($failedArchive))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$totalSize += (int) @filesize($failedArchive);
|
||||
Platform::getInstance()->unlink($failedArchive);
|
||||
}
|
||||
|
||||
// Mark the backup failed
|
||||
$running['status'] = 'fail';
|
||||
$running['instep'] = 0;
|
||||
$running['total_size'] = empty($running['total_size']) ? $totalSize : $running['total_size'];
|
||||
$running['multipart'] = 0;
|
||||
|
||||
Platform::getInstance()->set_or_update_statistics($running['id'], $running);
|
||||
|
||||
// Remove the temporary data
|
||||
$backupId = isset($running['backupid']) ? ('.' . $running['backupid']) : '';
|
||||
|
||||
self::removeTemporaryData($running['origin'] . $backupId);
|
||||
}
|
||||
|
||||
// Reload the factory
|
||||
static::unserialize($factory);
|
||||
unset($factory);
|
||||
|
||||
// Unpause logging if it was previously paused
|
||||
if (!$config->log)
|
||||
{
|
||||
Factory::getLog()->unpause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Akeeba Configuration object
|
||||
*
|
||||
* @return Configuration The Akeeba Configuration object
|
||||
*/
|
||||
public static function getConfiguration()
|
||||
{
|
||||
return static::getObjectInstance('Configuration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a statistics object, used to track current backup's progress
|
||||
*
|
||||
* @return Statistics
|
||||
*/
|
||||
public static function getStatistics()
|
||||
{
|
||||
return static::getObjectInstance('Util\\Statistics');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently configured archiver engine
|
||||
*
|
||||
* @param bool $reset Should I try to forcible create a new instance?
|
||||
*
|
||||
* @return Archiver\Base
|
||||
*/
|
||||
public static function getArchiverEngine($reset = false)
|
||||
{
|
||||
return static::getEngineInstance(
|
||||
'archiver', 'akeeba.advanced.archiver_engine',
|
||||
'Archiver\\', 'Archiver\\Jpa',
|
||||
$reset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently configured dump engine
|
||||
*
|
||||
* @param boolean $reset Should I try to forcible create a new instance?
|
||||
*
|
||||
* @return Dump\Base
|
||||
*/
|
||||
public static function getDumpEngine($reset = false)
|
||||
{
|
||||
return static::getEngineInstance(
|
||||
'dump', 'akeeba.advanced.dump_engine',
|
||||
'Dump\\', 'Dump\\Native',
|
||||
$reset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem scanner engine instance
|
||||
*
|
||||
* @param bool $reset Should I try to forcible create a new instance?
|
||||
*
|
||||
* @return Scan\Base The scanner engine
|
||||
*/
|
||||
public static function getScanEngine($reset = false)
|
||||
{
|
||||
return static::getEngineInstance(
|
||||
'scan', 'akeeba.advanced.scan_engine',
|
||||
'Scan\\', 'Scan\\Large',
|
||||
$reset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current post-processing engine. If no class is specified we
|
||||
* return the post-processing engine configured in akeeba.advanced.postproc_engine
|
||||
*
|
||||
* @param string $engine The name of the post-processing class to forcibly return
|
||||
*
|
||||
* @return PostProcInterface
|
||||
*/
|
||||
public static function getPostprocEngine($engine = null)
|
||||
{
|
||||
if (!is_null($engine))
|
||||
{
|
||||
static::$engineClassnames['postproc'] = 'Postproc\\' . ucfirst($engine);
|
||||
|
||||
return static::getObjectInstance(static::$engineClassnames['postproc']);
|
||||
}
|
||||
|
||||
return static::getEngineInstance(
|
||||
'postproc', 'akeeba.advanced.postproc_engine',
|
||||
'Postproc\\', 'Postproc\\None',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Core objects which are part of the engine state
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Returns an instance of the Filters feature class
|
||||
*
|
||||
* @return Filters The Filters feature class' object instance
|
||||
*/
|
||||
public static function getFilters()
|
||||
{
|
||||
return static::getObjectInstance('Core\\Filters');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the specified filter group class. Do note that it does not
|
||||
* work with platform filter classes. They are handled internally by AECoreFilters.
|
||||
*
|
||||
* @param string $filter_name The filter class to load, without AEFilter prefix
|
||||
*
|
||||
* @return Filter\Base The filter class' object instance
|
||||
*/
|
||||
public static function getFilterObject($filter_name)
|
||||
{
|
||||
return static::getObjectInstance('Filter\\' . ucfirst($filter_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an engine domain class and returns its associated object
|
||||
*
|
||||
* @param string $domain_name The name of the domain, e.g. installer for AECoreDomainInstaller
|
||||
*
|
||||
* @return Part
|
||||
*/
|
||||
public static function getDomainObject($domain_name)
|
||||
{
|
||||
return static::getObjectInstance('Core\\Domain\\' . ucfirst($domain_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a database connection object. It's an alias of AECoreDatabase::getDatabase()
|
||||
*
|
||||
* @param array $options Options to use when instantiating the database connection
|
||||
*
|
||||
* @return Base
|
||||
*/
|
||||
public static function getDatabase($options = null)
|
||||
{
|
||||
if (is_null($options))
|
||||
{
|
||||
$options = Platform::getInstance()->get_platform_database_options();
|
||||
}
|
||||
|
||||
if (isset($options['username']) && !isset($options['user']))
|
||||
{
|
||||
$options['user'] = $options['username'];
|
||||
}
|
||||
|
||||
return Database::getDatabase($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a database connection object. It's an alias of AECoreDatabase::getDatabase()
|
||||
*
|
||||
* @param array $options Options to use when instantiating the database connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function unsetDatabase($options = null)
|
||||
{
|
||||
if (is_null($options))
|
||||
{
|
||||
$options = Platform::getInstance()->get_platform_database_options();
|
||||
}
|
||||
|
||||
$db = Database::getDatabase($options);
|
||||
$db->close();
|
||||
|
||||
Database::unsetDatabase($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the a reference to the Akeeba Engine's timer
|
||||
*
|
||||
* @return Timer
|
||||
*/
|
||||
public static function getTimer()
|
||||
{
|
||||
return static::getObjectInstance('Core\\Timer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to Akeeba Engine's main controller called Kettenrad
|
||||
*
|
||||
* @return Kettenrad
|
||||
*/
|
||||
public static function getKettenrad()
|
||||
{
|
||||
return static::getObjectInstance('Core\\Kettenrad');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the factory storage class (formerly Tempvars)
|
||||
*
|
||||
* @return FactoryStorage
|
||||
*/
|
||||
public static function getFactoryStorage()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\FactoryStorage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the encryption class
|
||||
*
|
||||
* @return Encrypt
|
||||
*/
|
||||
public static function getEncryption()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\Encrypt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the crypto-safe random value generator class
|
||||
*
|
||||
* @return RandomValue
|
||||
*/
|
||||
public static function getRandval()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\RandomValue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the filesystem tools class
|
||||
*
|
||||
* @return FileSystem
|
||||
*/
|
||||
public static function getFilesystemTools()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\FileSystem');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the filesystem tools class
|
||||
*
|
||||
* @return FileLister
|
||||
*/
|
||||
public static function getFileLister()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\FileLister');
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Temporary objects which are not part of the engine state
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Returns an instance of the engine parameters provider which provides information on scripting, GUI configuration
|
||||
* elements and engine parts
|
||||
*
|
||||
* @return EngineParameters
|
||||
*/
|
||||
public static function getEngineParamsProvider()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\EngineParameters');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the log object
|
||||
*
|
||||
* @return Logger
|
||||
*/
|
||||
public static function getLog()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\Logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the configuration checks object
|
||||
*
|
||||
* @return ConfigurationCheck
|
||||
*/
|
||||
public static function getConfigurationChecks()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\ConfigurationCheck');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the secure settings handling object
|
||||
*
|
||||
* @return SecureSettings
|
||||
*/
|
||||
public static function getSecureSettings()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\SecureSettings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the secure settings handling object
|
||||
*
|
||||
* @return TemporaryFiles
|
||||
*/
|
||||
public static function getTempFiles()
|
||||
{
|
||||
return static::getTempObjectInstance('Util\\TemporaryFiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connector object for push messages
|
||||
*
|
||||
* @return PushMessages
|
||||
*/
|
||||
public static function getPush()
|
||||
{
|
||||
return static::getObjectInstance(self::$pushClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the push notifications helper class to use with this factory
|
||||
*
|
||||
* @param string $className The classname to use
|
||||
*
|
||||
* @since 9.3.1
|
||||
*/
|
||||
public static function setPushClass(string $className)
|
||||
{
|
||||
self::$pushClassName = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute path to Akeeba Engine's installation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAkeebaRoot()
|
||||
{
|
||||
if (empty(static::$root))
|
||||
{
|
||||
static::$root = __DIR__;
|
||||
}
|
||||
|
||||
return static::$root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $engineType Engine type, e.g. 'archiver', 'postproc', ...
|
||||
* @param string $configKey Profile config key with configured engine e.g. 'akeeba.advanced.archiver_engine'
|
||||
* @param string $prefix Prefix for engine classes, e.g. 'Archiver\\'
|
||||
* @param string $fallback Fallback class if the configured one doesn't exist e.g. 'Archiver\\Jpa'. Empty for
|
||||
* no fallback.
|
||||
* @param bool $reset Should I force-reload the engine? Default: false.
|
||||
*
|
||||
* @return mixed The Singleton engine object instance
|
||||
*/
|
||||
protected static function getEngineInstance($engineType, $configKey, $prefix, $fallback, $reset = false)
|
||||
{
|
||||
if (!$reset && !empty(static::$engineClassnames[$engineType]))
|
||||
{
|
||||
return static::getObjectInstance(static::$engineClassnames[$engineType]);
|
||||
}
|
||||
|
||||
// Unset the existing engine object
|
||||
if (!empty(static::$engineClassnames[$engineType]))
|
||||
{
|
||||
static::unsetObjectInstance(static::$engineClassnames[$engineType]);
|
||||
}
|
||||
|
||||
// Get the engine name from the backup profile, construct a class name and check if it exists
|
||||
$registry = static::getConfiguration();
|
||||
$engine = $registry->get($configKey);
|
||||
static::$engineClassnames[$engineType] = $prefix . ucfirst($engine);
|
||||
$object = static::getObjectInstance(static::$engineClassnames[$engineType]);
|
||||
|
||||
// If the engine object does not exist, fall back to the default
|
||||
if (!empty($fallback) && $object === false)
|
||||
{
|
||||
static::unsetObjectInstance(static::$engineClassnames[$engineType]);
|
||||
|
||||
static::$engineClassnames[$engineType] = $fallback;
|
||||
}
|
||||
|
||||
return static::getObjectInstance(static::$engineClassnames[$engineType]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function which instantiates an object of a class named $class_name.
|
||||
*
|
||||
* @param string $class_name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function getObjectInstance($class_name)
|
||||
{
|
||||
$class_name = trim($class_name, '\\');
|
||||
|
||||
if (isset(static::$objectList[$class_name]))
|
||||
{
|
||||
return static::$objectList[$class_name];
|
||||
}
|
||||
|
||||
static::$objectList[$class_name] = false;
|
||||
|
||||
$searchClass = '\\Akeeba\\Engine\\' . $class_name;
|
||||
|
||||
if (class_exists($searchClass))
|
||||
{
|
||||
static::$objectList[$class_name] = new $searchClass;
|
||||
}
|
||||
elseif (class_exists($class_name))
|
||||
{
|
||||
static::$objectList[$class_name] = new $class_name;
|
||||
}
|
||||
|
||||
return static::$objectList[$class_name];
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Handy functions
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Internal function which removes the object of the class named $class_name
|
||||
*
|
||||
* @param string $class_name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function unsetObjectInstance($class_name)
|
||||
{
|
||||
if (isset(static::$objectList[$class_name]))
|
||||
{
|
||||
static::$objectList[$class_name] = null;
|
||||
unset(static::$objectList[$class_name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function which instantiates an object of a class named $class_name. This is a temporary instance which
|
||||
* will not survive serialisation and subsequent unserialisation.
|
||||
*
|
||||
* @param string $class_name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function getTempObjectInstance($class_name)
|
||||
{
|
||||
$class_name = trim($class_name, '\\');
|
||||
|
||||
if (!isset(static::$temporaryObjectList[$class_name]))
|
||||
{
|
||||
static::$temporaryObjectList[$class_name] = false;
|
||||
|
||||
$searchClassname = '\\Akeeba\\Engine\\' . $class_name;
|
||||
|
||||
if (class_exists($searchClassname))
|
||||
{
|
||||
static::$temporaryObjectList[$class_name] = new $searchClassname;
|
||||
}
|
||||
}
|
||||
|
||||
return static::$temporaryObjectList[$class_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote the temporary data for a specific backup tag.
|
||||
*
|
||||
* @param string $originTag The backup tag to reset e.g. 'backend.id123' or 'frontend'.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function removeTemporaryData($originTag)
|
||||
{
|
||||
static::loadState($originTag, null, false);
|
||||
// Remove temporary files
|
||||
Factory::getTempFiles()->deleteTempFiles();
|
||||
// Delete any stale temporary data
|
||||
static::getFactoryStorage()->reset($originTag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout handler. It is registered as a global PHP shutdown function.
|
||||
*
|
||||
* If a PHP reports a timeout we will log this before letting PHP kill us.
|
||||
*/
|
||||
function AkeebaTimeoutTrap()
|
||||
{
|
||||
if (connection_status() >= 2)
|
||||
{
|
||||
Factory::getLog()->error('Akeeba Engine has timed out');
|
||||
}
|
||||
}
|
||||
|
||||
register_shutdown_function("\\Akeeba\\Engine\\AkeebaTimeoutTrap");
|
||||
Reference in New Issue
Block a user