392 lines
9.6 KiB
PHP
392 lines
9.6 KiB
PHP
<?php
|
|
/**
|
|
* @package solo
|
|
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU GPL version 3 or later
|
|
*/
|
|
|
|
// Protection against direct access
|
|
defined('AKEEBAENGINE') or die();
|
|
|
|
/**
|
|
* The superclass of all Akeeba Engine parts. The "parts" are intelligent stateful
|
|
* classes which perform a single procedure and have preparation, running and
|
|
* finalization phases. The transition between phases is handled automatically by
|
|
* this superclass' tick() final public method, which should be the ONLY public API
|
|
* exposed to the rest of the Akeeba Engine.
|
|
*/
|
|
abstract class AliceAbstractPart extends AliceAbstractObject
|
|
{
|
|
/**
|
|
* Indicates whether this part has finished its initialisation cycle
|
|
* @var boolean
|
|
*/
|
|
protected $isPrepared = false;
|
|
|
|
/**
|
|
* Indicates whether this part has more work to do (it's in running state)
|
|
* @var boolean
|
|
*/
|
|
protected $isRunning = false;
|
|
|
|
/**
|
|
* Indicates whether this part has finished its finalization cycle
|
|
* @var boolean
|
|
*/
|
|
protected $isFinished = false;
|
|
|
|
/**
|
|
* Indicates whether this part has finished its run cycle
|
|
* @var boolean
|
|
*/
|
|
protected $hasRan = false;
|
|
|
|
/**
|
|
* The name of the engine part (a.k.a. Domain), used in return table
|
|
* generation.
|
|
* @var string
|
|
*/
|
|
protected $active_domain = "";
|
|
|
|
/**
|
|
* The step this engine part is in. Used verbatim in return table and
|
|
* should be set by the code in the _run() method.
|
|
* @var string
|
|
*/
|
|
protected $active_step = "";
|
|
|
|
/**
|
|
* A more detailed description of the step this engine part is in. Used
|
|
* verbatim in return table and should be set by the code in the _run()
|
|
* method.
|
|
* @var string
|
|
*/
|
|
protected $active_substep = "";
|
|
|
|
/**
|
|
* Any configuration variables, in the form of an array.
|
|
* @var array
|
|
*/
|
|
protected $_parametersArray = array();
|
|
|
|
/** @var string The database root key */
|
|
protected $databaseRoot = array();
|
|
|
|
/** @var int Last reported warnings's position in array */
|
|
private $warnings_pointer = -1;
|
|
|
|
/** @var bool Should we log the step nesting? */
|
|
protected $nest_logging = false;
|
|
|
|
/** @var object Embedded installer preferences */
|
|
protected $installerSettings;
|
|
|
|
/**
|
|
* Public constructor
|
|
*/
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Runs the preparation for this part. Should set _isPrepared
|
|
* to true
|
|
*/
|
|
abstract protected function _prepare();
|
|
|
|
/**
|
|
* Runs the finalisation process for this part. Should set
|
|
* _isFinished to true.
|
|
*/
|
|
abstract protected function _finalize();
|
|
|
|
/**
|
|
* Runs the main functionality loop for this part. Upon calling,
|
|
* should set the _isRunning to true. When it finished, should set
|
|
* the _hasRan to true. If an error is encountered, setError should
|
|
* be used.
|
|
*/
|
|
abstract protected function _run();
|
|
|
|
/**
|
|
* Sets the BREAKFLAG, which instructs this engine part that the current step must break immediately,
|
|
* in fear of timing out.
|
|
*/
|
|
protected function setBreakFlag()
|
|
{
|
|
$registry = AliceFactory::getConfiguration();
|
|
$registry->set('volatile.breakflag', true);
|
|
}
|
|
|
|
/**
|
|
* Sets the engine part's internal state, in an easy to use manner
|
|
*
|
|
* @param string $state One of init, prepared, running, postrun, finished, error
|
|
* @param string $errorMessage The reported error message, should the state be set to error
|
|
*/
|
|
protected function setState($state = 'init', $errorMessage='Invalid setState argument')
|
|
{
|
|
switch($state)
|
|
{
|
|
case 'init':
|
|
$this->isPrepared = false;
|
|
$this->isRunning = false;
|
|
$this->isFinished = false;
|
|
$this->hasRun = false;
|
|
break;
|
|
|
|
case 'prepared':
|
|
$this->isPrepared = true;
|
|
$this->isRunning = false;
|
|
$this->isFinished = false;
|
|
$this->hasRun = false;
|
|
break;
|
|
|
|
case 'running':
|
|
$this->isPrepared = true;
|
|
$this->isRunning = true;
|
|
$this->isFinished = false;
|
|
$this->hasRun = false;
|
|
break;
|
|
|
|
case 'postrun':
|
|
$this->isPrepared = true;
|
|
$this->isRunning = false;
|
|
$this->isFinished = false;
|
|
$this->hasRun = true;
|
|
break;
|
|
|
|
case 'finished':
|
|
$this->isPrepared = true;
|
|
$this->isRunning = false;
|
|
$this->isFinished = true;
|
|
$this->hasRun = false;
|
|
break;
|
|
|
|
case 'error':
|
|
default:
|
|
$this->setError($errorMessage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The public interface to an engine part. This method takes care for
|
|
* calling the correct method in order to perform the initialisation -
|
|
* run - finalisation cycle of operation and return a proper reponse array.
|
|
*
|
|
* @param int $nesting
|
|
*
|
|
* @return array A Reponse Array
|
|
*/
|
|
final public function tick($nesting = 0)
|
|
{
|
|
$configuration = AliceFactory::getConfiguration();
|
|
$timer = AliceFactory::getTimer();
|
|
|
|
// Call the right action method, depending on engine part state
|
|
switch( $this->getState() )
|
|
{
|
|
case "init":
|
|
$this->_prepare();
|
|
$breakFlag = $configuration->set('volatile.breakflag', false);
|
|
break;
|
|
case "prepared":
|
|
$this->_run();
|
|
break;
|
|
case "running":
|
|
$this->_run();
|
|
break;
|
|
case "postrun":
|
|
$this->_finalize();
|
|
$breakFlag = $configuration->set('volatile.breakflag', false);
|
|
break;
|
|
}
|
|
|
|
// If there is still time, we are not finished and there is no break flag set, re-run the tick()
|
|
// method.
|
|
$breakFlag = $configuration->get('volatile.breakflag', false);
|
|
|
|
if(
|
|
!in_array( $this->getState(), array('finished','error') ) &&
|
|
( $timer->getTimeLeft() > 0 ) &&
|
|
!$breakFlag &&
|
|
($nesting < 20) &&
|
|
($this->nest_logging)
|
|
)
|
|
{
|
|
// Nesting is only applied if $this->nest_logging == true (currently only Kettenrad has this)
|
|
$nesting++;
|
|
if($this->nest_logging) AliceUtilLogger::WriteLog(_AE_LOG_DEBUG, "*** Batching successive steps (nesting level $nesting)");
|
|
$out = $this->tick($nesting);
|
|
}
|
|
else
|
|
{
|
|
// Return the output array
|
|
$out = $this->_makeReturnTable();
|
|
// Things to do for nest-logged parts (currently, only Kettenrad is)
|
|
if($this->nest_logging) {
|
|
if($breakFlag) AliceUtilLogger::WriteLog(_AE_LOG_DEBUG, "*** Engine steps batching: Break flag detected.");
|
|
// Reset the break flag
|
|
$configuration->set('volatile.breakflag', false);
|
|
// Log that we're breaking the step
|
|
AliceUtilLogger::WriteLog(_AE_LOG_DEBUG, "*** Batching of engine steps finished. I will now return control to the caller.");
|
|
// Enforce minimum execution time
|
|
$timer = AliceFactory::getTimer();
|
|
$timer->enforce_min_exec_time(true);
|
|
}
|
|
}
|
|
|
|
// Send a Return Table back to the caller
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Returns a copy of the class's status array
|
|
* @return array
|
|
*/
|
|
public function getStatusArray()
|
|
{
|
|
return $this->_makeReturnTable();
|
|
}
|
|
|
|
/**
|
|
* Sends any kind of setup information to the engine part. Using this,
|
|
* we avoid passing parameters to the constructor of the class. These
|
|
* parameters should be passed as an indexed array and should be taken
|
|
* into account during the preparation process only. This function will
|
|
* set the error flag if it's called after the engine part is prepared.
|
|
*
|
|
* @param array $parametersArray The parameters to be passed to the
|
|
* engine part.
|
|
*/
|
|
final public function setup( $parametersArray )
|
|
{
|
|
if( $this->isPrepared )
|
|
{
|
|
$this->setState('error', "Can't modify configuration after the preparation of " . $this->active_domain);
|
|
}
|
|
else
|
|
{
|
|
$this->_parametersArray = $parametersArray;
|
|
if(array_key_exists('root', $parametersArray))
|
|
{
|
|
$this->databaseRoot = $parametersArray['root'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the state of this engine part.
|
|
*
|
|
* @return string The state of this engine part. It can be one of
|
|
* error, init, prepared, running, postrun, finished.
|
|
*/
|
|
final public function getState()
|
|
{
|
|
if( $this->getError() )
|
|
{
|
|
return "error";
|
|
}
|
|
|
|
if( !($this->isPrepared) )
|
|
{
|
|
return "init";
|
|
}
|
|
|
|
if( !($this->isFinished) && !($this->isRunning) && !( $this->hasRun ) && ($this->isPrepared) )
|
|
{
|
|
return "prepared";
|
|
}
|
|
|
|
if ( !($this->isFinished) && $this->isRunning && !( $this->hasRun ) )
|
|
{
|
|
return "running";
|
|
}
|
|
|
|
if ( !($this->isFinished) && !($this->isRunning) && $this->hasRun )
|
|
{
|
|
return "postrun";
|
|
}
|
|
|
|
if ( $this->isFinished )
|
|
{
|
|
return "finished";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructs a Response Array based on the engine part's state.
|
|
* @return array The Response Array for the current state
|
|
*/
|
|
final protected function _makeReturnTable()
|
|
{
|
|
// Get a list of warnings
|
|
$warnings = $this->getWarnings();
|
|
// Report only new warnings if there is no warnings queue size
|
|
if( $this->_warnings_queue_size == 0 )
|
|
{
|
|
if( ($this->warnings_pointer > 0) && ($this->warnings_pointer < (count($warnings)) ) )
|
|
{
|
|
$warnings = array_slice($warnings, $this->warnings_pointer + 1);
|
|
$this->warnings_pointer += count($warnings);
|
|
}
|
|
else
|
|
{
|
|
$this->warnings_pointer = count($warnings);
|
|
}
|
|
}
|
|
|
|
$out = array(
|
|
'HasRun' => (!($this->isFinished)),
|
|
'Domain' => $this->active_domain,
|
|
'Step' => $this->active_step,
|
|
'Substep' => $this->active_substep,
|
|
'Error' => $this->getError(),
|
|
'Warnings' => $warnings
|
|
);
|
|
|
|
return $out;
|
|
}
|
|
|
|
final protected function setDomain($new_domain)
|
|
{
|
|
$this->active_domain = $new_domain;
|
|
}
|
|
|
|
final public function getDomain()
|
|
{
|
|
return $this->active_domain;
|
|
}
|
|
|
|
final protected function setStep($new_step)
|
|
{
|
|
$this->active_step = $new_step;
|
|
}
|
|
|
|
final public function getStep()
|
|
{
|
|
return $this->active_step;
|
|
}
|
|
|
|
final protected function setSubstep($new_substep)
|
|
{
|
|
$this->active_substep = $new_substep;
|
|
}
|
|
|
|
final public function getSubstep()
|
|
{
|
|
return $this->active_substep;
|
|
}
|
|
|
|
/**
|
|
* Implement this if your Engine Part can return the percentage of its work already complete
|
|
* @return float A number from 0 (nothing done) to 1 (all done)
|
|
*/
|
|
public function getProgress()
|
|
{
|
|
return 0;
|
|
}
|
|
}
|