first commit
This commit is contained in:
350
administrator/components/com_akeebabackup/engine/Platform.php
Normal file
350
administrator/components/com_akeebabackup/engine/Platform.php
Normal file
@@ -0,0 +1,350 @@
|
||||
<?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\Platform\Base;
|
||||
use Akeeba\Engine\Platform\PlatformInterface;
|
||||
use DirectoryIterator;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Platform abstraction. Manages the loading of platform connector objects and delegates calls to itself the them.
|
||||
*
|
||||
* @property string $tableNameProfiles The name of the table where backup profiles are stored
|
||||
* @property string $tableNameStats The name of the table where backup records are stored
|
||||
*
|
||||
* @since 3.4
|
||||
*/
|
||||
class Platform
|
||||
{
|
||||
/** @var Base|null The currently loaded platform connector object instance */
|
||||
protected static $platformConnectorInstance = null;
|
||||
|
||||
/** @var array A list of additional directories where platform classes can be found */
|
||||
protected static $knownPlatformsDirectories = [];
|
||||
|
||||
/** @var Platform The currently loaded object instance of this class. WARNING: This is NOT the platform connector! */
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Public class constructor
|
||||
*
|
||||
* @param string $platform Optional; platform name. Leave blank to auto-detect.
|
||||
*
|
||||
* @throws Exception When the platform cannot be loaded
|
||||
*/
|
||||
public function __construct($platform = null)
|
||||
{
|
||||
if (empty($platform) || is_null($platform))
|
||||
{
|
||||
$platform = static::detectPlatform();
|
||||
}
|
||||
|
||||
if (empty($platform))
|
||||
{
|
||||
throw new Exception('Can not find a suitable Akeeba Engine platform for your site');
|
||||
}
|
||||
|
||||
static::$platformConnectorInstance = static::loadPlatform($platform);
|
||||
|
||||
if (!is_object(static::$platformConnectorInstance))
|
||||
{
|
||||
throw new Exception("Can not load Akeeba Engine platform $platform");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the Singleton pattern for this class
|
||||
*
|
||||
* @staticvar Platform $instance The static object instance
|
||||
*
|
||||
* @param string $platform Optional; platform name. Autodetect if blank.
|
||||
*
|
||||
* @return PlatformInterface
|
||||
*/
|
||||
public static function &getInstance($platform = null)
|
||||
{
|
||||
if (!is_object(static::$instance))
|
||||
{
|
||||
static::$instance = new Platform($platform);
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all directories where platform classes can be found
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPlatformDirectories()
|
||||
{
|
||||
$defaultPath = [];
|
||||
|
||||
if (is_object(static::$platformConnectorInstance))
|
||||
{
|
||||
$defaultPath[] = __DIR__ . '/Platform/' . static::$platformConnectorInstance->platformName;
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
static::$knownPlatformsDirectories,
|
||||
$defaultPath
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists available platforms
|
||||
*
|
||||
* @staticvar array $platforms Static cache of the available platforms
|
||||
*
|
||||
* @return array The list of available platforms
|
||||
*/
|
||||
static public function listPlatforms()
|
||||
{
|
||||
if (empty(static::$knownPlatformsDirectories))
|
||||
{
|
||||
$di = new DirectoryIterator(__DIR__ . '/Platform');
|
||||
|
||||
/** @var DirectoryIterator $file */
|
||||
foreach ($di as $file)
|
||||
{
|
||||
if (!$file->isDir())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($file->isDot())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($file->getExtension() !== 'php')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$shortName = $file->getFilename();
|
||||
$bareName = basename($shortName, '.php');
|
||||
|
||||
/**
|
||||
* We never have dots in our filenames but some hosts will rename files similar to foo.1.php when their
|
||||
* broken security scanners detect a false positive. This is our defence against that.
|
||||
*/
|
||||
if (strpos($bareName, '.') !== false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
static::$knownPlatformsDirectories[$shortName] = $file->getRealPath();
|
||||
}
|
||||
}
|
||||
|
||||
return static::$knownPlatformsDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a platform to the list of known platforms
|
||||
*
|
||||
* @param string $slug Short name of the platform
|
||||
* @param string $platformDirectory The path where you can find it
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function addPlatform($slug, $platformDirectory)
|
||||
{
|
||||
if (empty(static::$knownPlatformsDirectories))
|
||||
{
|
||||
static::listPlatforms();
|
||||
|
||||
static::$knownPlatformsDirectories[$slug] = $platformDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect the suitable platform for this site
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Exception When no platform is detected
|
||||
*/
|
||||
protected static function detectPlatform()
|
||||
{
|
||||
$platforms = static::listPlatforms();
|
||||
|
||||
if (empty($platforms))
|
||||
{
|
||||
throw new Exception('No Akeeba Engine platform class found');
|
||||
}
|
||||
|
||||
$bestPlatform = (object) [
|
||||
'name' => null,
|
||||
'priority' => 0,
|
||||
];
|
||||
|
||||
foreach ($platforms as $platform => $path)
|
||||
{
|
||||
$o = static::loadPlatform($platform, $path);
|
||||
|
||||
if (is_null($o))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($o->isThisPlatform())
|
||||
{
|
||||
if ($o->priority > $bestPlatform->priority)
|
||||
{
|
||||
$bestPlatform->priority = $o->priority;
|
||||
$bestPlatform->name = $platform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bestPlatform->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a given platform and return the platform object
|
||||
*
|
||||
* @param string $platform Platform name
|
||||
* @param string $path The path to laod the platform from (optional)
|
||||
*
|
||||
* @return Base
|
||||
*/
|
||||
protected static function &loadPlatform($platform, $path = null)
|
||||
{
|
||||
if (empty($path))
|
||||
{
|
||||
if (isset(static::$knownPlatformsDirectories[$platform]))
|
||||
{
|
||||
$path = static::$knownPlatformsDirectories[$platform];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($path))
|
||||
{
|
||||
$path = dirname(__FILE__) . '/' . $platform;
|
||||
}
|
||||
|
||||
$classFile = $path . '/Platform.php';
|
||||
$className = '\\Akeeba\\Engine\\Platform\\' . ucfirst($platform);
|
||||
|
||||
$null = null;
|
||||
|
||||
if (!file_exists($classFile))
|
||||
{
|
||||
return $null;
|
||||
}
|
||||
|
||||
require_once($classFile);
|
||||
|
||||
if (!class_exists($className, false))
|
||||
{
|
||||
return $null;
|
||||
}
|
||||
|
||||
$o = new $className;
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to proxy all calls to the loaded platform object
|
||||
*
|
||||
* @param string $name The name of the method to call
|
||||
* @param array $arguments The arguments to pass
|
||||
*
|
||||
* @return mixed The result of the method being called
|
||||
*
|
||||
* @throws Exception When the platform isn't loaded or an non-existent method is called
|
||||
*/
|
||||
public function __call($name, array $arguments)
|
||||
{
|
||||
if (is_null(static::$platformConnectorInstance))
|
||||
{
|
||||
throw new Exception('Akeeba Engine platform is not loaded');
|
||||
}
|
||||
|
||||
if (method_exists(static::$platformConnectorInstance, $name))
|
||||
{
|
||||
// Call_user_func_array is ~3 times slower than direct method calls.
|
||||
// See the on-line PHP documentation page of call_user_func_array for more information.
|
||||
switch (count($arguments))
|
||||
{
|
||||
case 0 :
|
||||
$result = static::$platformConnectorInstance->$name();
|
||||
break;
|
||||
case 1 :
|
||||
$result = static::$platformConnectorInstance->$name($arguments[0]);
|
||||
break;
|
||||
case 2:
|
||||
$result = static::$platformConnectorInstance->$name($arguments[0], $arguments[1]);
|
||||
break;
|
||||
case 3:
|
||||
$result = static::$platformConnectorInstance->$name($arguments[0], $arguments[1], $arguments[2]);
|
||||
break;
|
||||
case 4:
|
||||
$result = static::$platformConnectorInstance->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
|
||||
break;
|
||||
case 5:
|
||||
$result = static::$platformConnectorInstance->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]);
|
||||
break;
|
||||
default:
|
||||
// Resort to using call_user_func_array for many segments
|
||||
$result = call_user_func_array([static::$platformConnectorInstance, $name], $arguments);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('Method ' . $name . ' not found in Akeeba Platform');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter for the properties of the loaded platform
|
||||
*
|
||||
* @param string $name The name of the property to get
|
||||
*
|
||||
* @return mixed The value of the property
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (!isset(static::$platformConnectorInstance->$name) || !property_exists(static::$platformConnectorInstance, $name))
|
||||
{
|
||||
static::$platformConnectorInstance->$name = null;
|
||||
user_error(__CLASS__ . ' does not support property ' . $name, E_NOTICE);
|
||||
}
|
||||
|
||||
return static::$platformConnectorInstance->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic setter for the properties of the loaded platform
|
||||
*
|
||||
* @param string $name The name of the property to set
|
||||
* @param mixed $value The value of the property to set
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if (isset(static::$platformConnectorInstance->$name) || property_exists(static::$platformConnectorInstance, $name))
|
||||
{
|
||||
static::$platformConnectorInstance->$name = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$platformConnectorInstance->$name = null;
|
||||
user_error(__CLASS__ . ' does not support property ' . $name, E_NOTICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user