| Server IP : 213.186.33.4 / Your IP : 216.73.216.193 Web Server : Apache System : Linux webm006.cluster103.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : awebpaca ( 35430) PHP Version : 8.5.0 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/awebpaca/visitevirtuelle/administrator/components/com_akeeba/akeeba/core/ |
Upload File : |
<?php
/**
* Akeeba Engine
* The modular PHP5 site backup engine
*
* @copyright Copyright (c)2009-2014 Nicholas K. Dionysopoulos
* @license GNU GPL version 3 or, at your option, any later version
* @package akeebaengine
*
*/
// Protection against direct access
defined('AKEEBAENGINE') or die();
/**
* This is Akeeba Engine's heart. Kettenrad is reponsible for launching the
* domain chain of a backup job.
*/
final class AECoreKettenrad extends AEAbstractPart
{
/** @var array Cached copy of the response array */
private $array_cache = null;
/** @var array The list of remaining steps */
private $domain_chain = array();
/** @var string The current domain's name */
private $domain = '';
/**@ var string The active domain's class name */
private $class = '';
/** @var string The current backup's tag (actually: the backup's origin) */
private $tag = null;
/** @var int How many steps the domain_chain array contained when the backup began. Used for percentage calculations. */
private $total_steps = 0;
/** @var string A unique backup ID which allows us to run multiple parallel backups using the same backup origin (tag) */
private $backup_id = '';
/**
* Returns the unique Backup ID
*
* @return string
*/
public function getBackupId()
{
return $this->backup_id;
}
/**
* Sets the unique backup ID.
*
* @param string $backup_id
*/
public function setBackupId($backup_id = null)
{
$this->backup_id = $backup_id;
}
/**
* Returns the current backup tag. If none is specified, it sets it to be the
* same as the current backup origin and returns the new setting.
*
* @return string
*/
public function getTag()
{
if (empty($this->tag))
{
// If no tag exists, we resort to the pre-set backup origin
$tag = AEPlatform::getInstance()->get_backup_origin();
$this->tag = $tag;
}
return $this->tag;
}
protected function _prepare()
{
// Intialize the timer class
$timer = AEFactory::getTimer();
// Do we have a tag?
if (!empty($this->_parametersArray['tag']))
{
$this->tag = $this->_parametersArray['tag'];
}
// Make sure a tag exists (or create a new one)
$this->tag = $this->getTag();
// Reset the log
$logTag = $this->getLogTag();
AEUtilLogger::openLog($logTag);
AEUtilLogger::ResetLog($logTag);
set_error_handler('akeebaBackupErrorHandler');
// Reset the storage
$tempVarsTag = $this->tag . (empty($this->backup_id) ? '' : ('.' . $this->backup_id));
AEUtilTempvars::reset($tempVarsTag);
// Apply the configuration overrides
$overrides = AEPlatform::getInstance()->configOverrides;
if (is_array($overrides) && @count($overrides))
{
$registry = AEFactory::getConfiguration();
$protected_keys = $registry->getProtectedKeys();
$registry->resetProtectedKeys();
foreach ($overrides as $k => $v)
{
$registry->set($k, $v);
}
$registry->setProtectedKeys($protected_keys);
}
// Get the domain chain
$this->domain_chain = AEUtilScripting::getDomainChain();
$this->total_steps = count($this->domain_chain) - 1; // Init shouldn't count in the progress bar
// Mark this engine for Nesting Logging
$this->nest_logging = true;
// Preparation is over
$this->array_cache = null;
$this->setState('prepared');
//restore_error_handler();
}
protected function _run()
{
$logTag = $this->getLogTag();
AEUtilLogger::openLog($logTag);
set_error_handler('akeebaBackupErrorHandler');
// Maybe we're already done or in an error state?
if (($this->getError()) || ($this->getState() == 'postrun'))
{
return;
}
// Set running state
$this->setState('running');
// Initialize operation counter
$registry = AEFactory::getConfiguration();
$registry->set('volatile.operation_counter', 0);
// Advance step counter
$stepCounter = $registry->get('volatile.step_counter', 0);
$registry->set('volatile.step_counter', ++$stepCounter);
// Log step start number
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '====== Starting Step number ' . $stepCounter . ' ======');
if (defined('AKEEBADEBUG'))
{
$root = AEPlatform::getInstance()->get_site_root();
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'Site root: ' . $root);
}
$timer = AEFactory::getTimer();
$finished = false;
$error = false;
$breakFlag = false; // BREAKFLAG is optionally passed by domains to force-break current operation
// Apply an infinite time limit if required
if ($registry->get('akeeba.tuning.settimelimit', 0))
{
if (function_exists('set_time_limit'))
{
set_time_limit(0);
}
}
// Loop until time's up, we're done or an error occurred, or BREAKFLAG is set
$this->array_cache = null;
while (($timer->getTimeLeft() > 0) && (!$finished) && (!$error) && (!$breakFlag))
{
// Reset the break flag
$registry->set('volatile.breakflag', false);
// Do we have to switch domains? This only happens if there is no active
// domain, or the current domain has finished
$have_to_switch = false;
$object = null;
if ($this->class == '')
{
$have_to_switch = true;
}
else
{
$object = AEFactory::getDomainObject($this->class);
if (!is_object($object))
{
$have_to_switch = true;
}
else
{
if (!in_array('getState', get_class_methods($object)))
{
$have_to_switch = true;
}
elseif ($object->getState() == 'finished')
{
$have_to_switch = true;
}
}
}
// Switch domain if necessary
if ($have_to_switch)
{
if (!AEFactory::getConfiguration()->get('akeeba.tuning.nobreak.domains', 0))
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: BREAKING STEP BEFORE SWITCHING DOMAIN");
$registry->set('volatile.breakflag', true);
}
// Free last domain
$object = null;
if (empty($this->domain_chain))
{
// Aw, we're done! No more domains to run.
$this->setState('postrun');
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: No more domains to process");
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '====== Finished Step number ' . $stepCounter . ' ======');
$this->array_cache = null;
//restore_error_handler();
return;
}
// Shift the next definition off the stack
$this->array_cache = null;
$new_definition = array_shift($this->domain_chain);
if (array_key_exists('class', $new_definition))
{
$this->domain = $new_definition['domain'];
$this->class = $new_definition['class'];
// Get a working object
$object = AEFactory::getDomainObject($this->class);
$object->setup($this->_parametersArray);
}
else
{
AEUtilLogger::WriteLog(_AE_LOG_WARNING, "Kettenrad :: No class defined trying to switch domains. The backup will crash.");
$this->domain = null;
$this->class = null;
}
}
else
{
if (!is_object($object))
{
$object = AEFactory::getDomainObject($this->class);
}
}
// Tick the object
$result = $object->tick();
// Propagate errors
$this->propagateFromObject($object);
// Advance operation counter
$currentOperationNumber = $registry->get('volatile.operation_counter', 0);
$currentOperationNumber++;
$registry->set('volatile.operation_counter', $currentOperationNumber);
// Process return array
$this->setDomain($this->domain);
$this->setStep($result['Step']);
$this->setSubstep($result['Substep']);
// Check for BREAKFLAG
$breakFlag = $registry->get('volatile.breakflag', false);
// Process errors
$error = false;
if ($this->getError())
{
$error = true;
}
// Check if the backup procedure should finish now
$finished = $error ? true : !($result['HasRun']);
// Log operation end
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '----- Finished operation ' . $currentOperationNumber . ' ------');
}
// Log the result
if (!$error)
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Successful Smart algorithm on " . get_class($object));
}
else
{
AEUtilLogger::WriteLog(_AE_LOG_ERROR, "Failed Smart algorithm on " . get_class($object));
}
// Log if we have to do more work or not
if (!is_object($object))
{
AEUtilLogger::WriteLog(_AE_LOG_WARNING, "Kettenrad :: Empty object found when processing domain '" . $this->domain . "'. This should never happen.");
}
else
{
if ($object->getState() == 'running')
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: More work required in domain '" . $this->domain . "'");
// We need to set the break flag for the part processing to not batch successive steps
$registry->set('volatile.breakflag', true);
}
elseif ($object->getState() == 'finished')
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: Domain '" . $this->domain . "' has finished.");
$registry->set('volatile.breakflag', false);
}
}
// Log step end
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, '====== Finished Step number ' . $stepCounter . ' ======');
if (!$registry->get('akeeba.tuning.nobreak.domains', 0))
{
// Force break between steps
$registry->set('volatile.breakflag', true);
}
//restore_error_handler();
}
protected function _finalize()
{
// Open the log
$logTag = $this->getLogTag();
AEUtilLogger::openLog($logTag);
//set_error_handler('akeebaBackupErrorHandler');
// Kill the cached array
$this->array_cache = null;
// Remove the memory file
$tempVarsTag = $this->tag . (empty($this->backup_id) ? '' : ('.' . $this->backup_id));
AEUtilTempvars::reset($tempVarsTag);
// All done.
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: Just finished");
$this->setState('finished');
//restore_error_handler();
}
/**
* Saves the whole factory to temporary storage
*/
/**
* Saves the whole factory to temporary storage
*
* @param string|null $tag The backup origin to save. Leave empty to get from already loaded Kettenrad instance.
* @param string|null $backupId The backup ID to save. Leave empty to get from already loaded Kettenrad instance.
*/
public static function save($tag = null, $backupId = null)
{
$kettenrad = AEFactory::getKettenrad();
if (empty($tag))
{
$tag = $kettenrad->tag;
}
if (empty($backupId))
{
$backupId = $kettenrad->backup_id;
}
$saveTag = $tag . (empty($backupId) ? '' : ('.' . $backupId));
$ret = $kettenrad->getStatusArray();
if ($ret['HasRun'] == 1)
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Will not save a finished Kettenrad instance");
}
else
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Saving Kettenrad instance $tag");
// Save a Factory snapshot
AEUtilTempvars::set(AEFactory::serialize(), $saveTag);
}
}
/**
* Loads the factory from the storage (if it exists) and returns a reference to the
* Kettenrad object.
*
* @param string|null $tag The backup origin to load
* @param string|null $backupId The backup ID to load
*
* @return AECoreKettenrad A reference to the Kettenrad object
*/
public static function &load($tag = null, $backupId = null)
{
if (is_null($tag) && defined('AKEEBA_BACKUP_ORIGIN'))
{
$tag = AKEEBA_BACKUP_ORIGIN;
}
if (is_null($backupId) && defined('AKEEBA_BACKUP_ID'))
{
$tag = AKEEBA_BACKUP_ID;
}
$loadTag = $tag . (empty($backupId) ? '' : ('.' . $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 = AEFactory::getConfiguration();
if (empty($config->activeProfile))
{
// Only bother loading a configuration if none has been already loaded
$statList = AEPlatform::getInstance()->get_statistics_list(array(
'filters' => array(
array('field' => 'tag', 'value' => $tag)
), 'order' => array(
'by' => 'id', 'order' => 'DESC'
)
)
);
if (is_array($statList))
{
$stat = array_pop($statList);
$profile = $stat['profile_id'];
AEPlatform::getInstance()->load_configuration($profile);
}
}
AEUtilLogger::openLog($loadTag);
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Kettenrad :: Attempting to load from database ($tag) [$loadTag]");
$serialized_factory = AEUtilTempvars::get($loadTag);
if ($serialized_factory !== false)
{
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, " -- Loaded stored Akeeba Factory ($tag) [$loadTag]");
AEFactory::unserialize($serialized_factory);
}
else
{
// There is no serialized factory. Nuke the in-memory factory.
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, " -- Stored Akeeba Factory ($tag) [$loadTag] not found - hard reset");
AEFactory::nuke();
AEPlatform::getInstance()->load_configuration();
}
unset($serialized_factory);
return AEFactory::getKettenrad();
}
/**
* Resets the Kettenrad state, wiping out any pending backups and/or stale
* temporary data.
*
* @param array $config Configuration parameters for the reset operation
*/
public static function reset($config = array())
{
$default_config = array(
'global' => true, // Reset all origins when true
'log' => false, // Log our actions
'maxrun' => 180, // Consider "pending" backups as failed after this many seconds
);
$config = (object)array_merge($default_config, $config);
// Pause logging if so desired
if (!$config->log)
{
AEUtilLogger::WriteLog(false, '');
}
$originTag = null;
if (!$config->global)
{
// If we're not resetting globally, get a list of running backups per tag
$originTag = AEPlatform::getInstance()->get_backup_origin();
}
// Cache the factory before proceeding
$factory = AEFactory::serialize();
$runningList = AEPlatform::getInstance()->get_running_backups($originTag);
// Origins we have to clean
$origins = array(
AEPlatform::getInstance()->get_backup_origin()
);
// 1. Detect failed backups
if (is_array($runningList) && !empty($runningList))
{
// The current timestamp
$now = time();
// Mark running backups as failed
foreach ($runningList as $running)
{
if (empty($originTag))
{
// Check the timestamp of the log file to decide if it's stuck,
// but only if a tag is not set
$tstamp = @filemtime(AEUtilLogger::logName($running['origin']));
if ($tstamp !== false)
{
// We can only check the timestamp if it's returned. If not, we assume the backup is stale
$difference = abs($now - $tstamp);
// Backups less than maxrun seconds old are not considered stale (default: 3 minutes)
if ($difference < $config->maxrun)
{
continue;
}
}
}
$filenames = AEUtilStatistics::get_all_filenames($running, false);
$totalSize = 0;
// Process if there are files to delete...
if (!is_null($filenames))
{
// Delete the failed backup's archive, if exists
foreach ($filenames as $failedArchive)
{
if (file_exists($failedArchive))
{
$totalSize += (int)@filesize($failedArchive);
AEPlatform::getInstance()->unlink($failedArchive);
}
}
}
// Mark the backup failed
if (!$running['total_size'])
{
$running['total_size'] = $totalSize;
}
$running['status'] = 'fail';
$running['multipart'] = 0;
$dummy = null;
AEPlatform::getInstance()->set_or_update_statistics($running['id'], $running, $dummy);
$backupId = isset($running['backupid']) ? ('.' . $running['backupid']) : '';
$origins[] = $running['origin'] . $backupId;
}
}
if (!empty($origins))
{
$origins = array_unique($origins);
foreach ($origins as $originTag)
{
AECoreKettenrad::load($originTag);
// Remove temporary files
AEUtilTempfiles::deleteTempFiles();
// Delete any stale temporary data
AEUtilTempvars::reset($originTag);
}
}
// Reload the factory
AEFactory::unserialize($factory);
unset($factory);
// Unpause logging if it was previously paused
if (!$config->log)
{
AEUtilLogger::WriteLog(true, '');
}
}
/**
* Returns a copy of the class's status array
*
* @return array
*/
public function getStatusArray()
{
if (empty($this->array_cache))
{
// Get the default table
$array = $this->_makeReturnTable();
// Get the current step number
$stepCounter = AEFactory::getConfiguration()->get('volatile.step_counter', 0);
// Add the archive name
$statistics = AEFactory::getStatistics();
$record = $statistics->getRecord();
$array['Archive'] = isset($record['archivename']) ? $record['archivename'] : '';
// Translate HasRun to what the rest of the suite expects
$array['HasRun'] = ($this->getState() == 'finished') ? 1 : 0;
// Translate no errors
$array['Error'] = ($array['Error'] == false) ? '' : $array['Error'];
$array['tag'] = $this->tag;
$array['Progress'] = $this->getProgress();
$array['backupid'] = $this->getBackupId();
$array['sleepTime'] = $this->waitTimeMsec;
$array['stepNumber'] = $stepCounter;
$array['stepState'] = $this->getState();
$this->array_cache = $array;
}
return $this->array_cache;
}
/**
* Gets the percentage of the backup process done so far.
*
* @return string
*/
public function getProgress()
{
// Get the overall percentage (based on domains complete so far)
$remaining_steps = count($this->domain_chain);
$remaining_steps++;
$overall = 1 - ($remaining_steps / $this->total_steps);
// How much is this step worth?
$this_max = 1 / $this->total_steps;
// Get the percentage done of the current object
if (!empty($this->class))
{
$object = AEFactory::getDomainObject($this->class);
}
else
{
$object = null;
}
if (!is_object($object))
{
$local = 0;
}
else
{
$local = $object->getProgress();
}
$percentage = (int)(100 * ($overall + $local * $this_max));
if ($percentage < 0)
{
$percentage = 0;
}
elseif ($percentage > 100)
{
$percentage = 100;
}
return $percentage;
}
/**
* Returns the tag used to open the correct log file
*
* @return string
*/
protected function getLogTag()
{
$tag = $this->getTag();
if (!empty($this->backup_id))
{
$tag .= '.' . $this->backup_id;
}
return $tag;
}
}
/**
* Timeout error handler
*/
function deadOnTimeOut()
{
if (connection_status() == 1)
{
AEUtilLogger::WriteLog(_AE_LOG_ERROR, 'The process was aborted on user\'s request');
}
elseif (connection_status() >= 2)
{
AEUtilLogger::WriteLog(_AE_LOG_ERROR, AEPlatform::getInstance()->translate('KETTENRAD_TIMEOUT'));
}
}
register_shutdown_function("deadOnTimeOut");
/**
* Nifty trick to track and log PHP errors to Akeeba Backup's log
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
*
* @return bool|null
*/
function akeebaBackupErrorHandler($errno, $errstr, $errfile, $errline)
{
// Sanity check
if (!function_exists('error_reporting'))
{
return false;
}
// Do not proceed if the error springs from an @function() construct, or if
// the overall error reporting level is set to report no errors.
$error_reporting = error_reporting();
if ($error_reporting == 0)
{
return false;
}
switch ($errno)
{
case E_ERROR:
case E_USER_ERROR:
// Can I really catch fatal errors? It doesn't seem likely...
AEUtilLogger::WriteLog(_AE_LOG_ERROR, "PHP FATAL ERROR on line $errline in file $errfile:");
AEUtilLogger::WriteLog(_AE_LOG_ERROR, $errstr);
AEUtilLogger::WriteLog(_AE_LOG_ERROR, "Execution aborted due to PHP fatal error");
break;
case E_WARNING:
case E_USER_WARNING:
// Log as debug messages so that we don't spook the user with warnings
AEUtilLogger::WriteLog(_AE_LOG_WARNING, "PHP WARNING on line $errline in file $errfile:");
AEUtilLogger::WriteLog(_AE_LOG_WARNING, $errstr);
break;
case E_NOTICE:
case E_USER_NOTICE:
// Log as debug messages so that we don't spook the user with notices
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "PHP NOTICE on line $errline in file $errfile:");
AEUtilLogger::WriteLog(_AE_LOG_DEBUG, $errstr);
break;
default:
// These are E_DEPRECATED, E_STRICT etc. Ignore that crap.
break;
}
// Don't execute PHP's internal error handler
//return true;
}