Newwebshop/app/Core/Plugin.php

764 lines
21 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* Plugin-System für PrestaShop-Modul-Kompatibilität
*
* @author Webshop System
* @license GPL v3
*/
namespace App\Core;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
class Plugin
{
private static $instance = null;
private $plugins = [];
private $eventDispatcher;
private $cache;
private $logger;
private $enabled = true;
private function __construct()
{
$this->eventDispatcher = EventDispatcher::getInstance();
$this->cache = Cache::getInstance();
$this->logger = Logger::getInstance();
$this->loadPlugins();
}
/**
* Singleton-Instanz abrufen
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Plugins laden
*/
private function loadPlugins()
{
$pluginsDir = __DIR__ . '/../../../plugins/';
if (!is_dir($pluginsDir)) {
mkdir($pluginsDir, 0755, true);
return;
}
$pluginDirs = scandir($pluginsDir);
foreach ($pluginDirs as $dir) {
if ($dir !== '.' && $dir !== '..' && is_dir($pluginsDir . $dir)) {
$configFile = $pluginsDir . $dir . '/plugin.json';
if (file_exists($configFile)) {
$config = json_decode(file_get_contents($configFile), true);
if ($config) {
$this->plugins[$dir] = array_merge($config, [
'directory' => $dir,
'path' => $pluginsDir . $dir,
'active' => $config['active'] ?? false,
'version' => $config['version'] ?? '1.0.0',
'dependencies' => $config['dependencies'] ?? [],
'hooks' => $config['hooks'] ?? [],
'settings' => $config['settings'] ?? []
]);
}
}
}
}
}
/**
* Plugin registrieren
*/
public function registerPlugin($name, $config)
{
$plugin = array_merge($config, [
'name' => $name,
'active' => $config['active'] ?? false,
'version' => $config['version'] ?? '1.0.0',
'dependencies' => $config['dependencies'] ?? [],
'hooks' => $config['hooks'] ?? [],
'settings' => $config['settings'] ?? []
]);
$this->plugins[$name] = $plugin;
// Plugin in Datenbank speichern
$this->savePluginToDatabase($name, $plugin);
// Event auslösen
$this->eventDispatcher->dispatch('plugin.register', [
'plugin_name' => $name,
'plugin_config' => $plugin
]);
$this->logger->info('Plugin registriert', [
'plugin_name' => $name,
'version' => $plugin['version']
]);
return $this;
}
/**
* Plugin aktivieren
*/
public function activatePlugin($name)
{
if (!isset($this->plugins[$name])) {
return false;
}
$plugin = $this->plugins[$name];
// Dependencies prüfen
if (!$this->checkDependencies($plugin['dependencies'])) {
$this->logger->error('Plugin-Aktivierung fehlgeschlagen - Dependencies nicht erfüllt', [
'plugin_name' => $name,
'dependencies' => $plugin['dependencies']
]);
return false;
}
// Plugin-Klasse laden
$pluginClass = $this->loadPluginClass($name);
if (!$pluginClass) {
return false;
}
try {
// Plugin initialisieren
$instance = new $pluginClass();
if (method_exists($instance, 'activate')) {
$instance->activate();
}
// Hooks registrieren
$this->registerPluginHooks($name, $plugin['hooks']);
// Plugin als aktiv markieren
$this->plugins[$name]['active'] = true;
$this->plugins[$name]['instance'] = $instance;
// Datenbank aktualisieren
$this->updatePluginStatus($name, true);
// Event auslösen
$this->eventDispatcher->dispatch('plugin.activate', [
'plugin_name' => $name,
'plugin_config' => $plugin
]);
$this->logger->info('Plugin aktiviert', [
'plugin_name' => $name,
'version' => $plugin['version']
]);
return true;
} catch (\Exception $e) {
$this->logger->error('Plugin-Aktivierung Fehler', [
'plugin_name' => $name,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Plugin deaktivieren
*/
public function deactivatePlugin($name)
{
if (!isset($this->plugins[$name]) || !$this->plugins[$name]['active']) {
return false;
}
$plugin = $this->plugins[$name];
try {
// Plugin-Instance deaktivieren
if (isset($plugin['instance']) && method_exists($plugin['instance'], 'deactivate')) {
$plugin['instance']->deactivate();
}
// Hooks deregistrieren
$this->unregisterPluginHooks($name);
// Plugin als inaktiv markieren
$this->plugins[$name]['active'] = false;
unset($this->plugins[$name]['instance']);
// Datenbank aktualisieren
$this->updatePluginStatus($name, false);
// Event auslösen
$this->eventDispatcher->dispatch('plugin.deactivate', [
'plugin_name' => $name,
'plugin_config' => $plugin
]);
$this->logger->info('Plugin deaktiviert', [
'plugin_name' => $name,
'version' => $plugin['version']
]);
return true;
} catch (\Exception $e) {
$this->logger->error('Plugin-Deaktivierung Fehler', [
'plugin_name' => $name,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Plugin löschen
*/
public function deletePlugin($name)
{
if (!isset($this->plugins[$name])) {
return false;
}
// Plugin deaktivieren falls aktiv
if ($this->plugins[$name]['active']) {
$this->deactivatePlugin($name);
}
try {
// Plugin-Verzeichnis löschen
$pluginPath = $this->plugins[$name]['path'];
if (is_dir($pluginPath)) {
$this->removeDirectory($pluginPath);
}
// Plugin aus Array entfernen
unset($this->plugins[$name]);
// Plugin aus Datenbank entfernen
$this->deletePluginFromDatabase($name);
// Event auslösen
$this->eventDispatcher->dispatch('plugin.delete', [
'plugin_name' => $name
]);
$this->logger->info('Plugin gelöscht', [
'plugin_name' => $name
]);
return true;
} catch (\Exception $e) {
$this->logger->error('Plugin-Löschung Fehler', [
'plugin_name' => $name,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Plugin-Update
*/
public function updatePlugin($name, $newVersion)
{
if (!isset($this->plugins[$name])) {
return false;
}
$plugin = $this->plugins[$name];
$wasActive = $plugin['active'];
try {
// Plugin deaktivieren falls aktiv
if ($wasActive) {
$this->deactivatePlugin($name);
}
// Update-Logic hier implementieren
// (Download, Backup, Install, etc.)
// Plugin-Konfiguration aktualisieren
$this->plugins[$name]['version'] = $newVersion;
$this->plugins[$name]['updated_at'] = date('Y-m-d H:i:s');
// Datenbank aktualisieren
$this->updatePluginVersion($name, $newVersion);
// Plugin wieder aktivieren falls es vorher aktiv war
if ($wasActive) {
$this->activatePlugin($name);
}
// Event auslösen
$this->eventDispatcher->dispatch('plugin.update', [
'plugin_name' => $name,
'old_version' => $plugin['version'],
'new_version' => $newVersion
]);
$this->logger->info('Plugin aktualisiert', [
'plugin_name' => $name,
'old_version' => $plugin['version'],
'new_version' => $newVersion
]);
return true;
} catch (\Exception $e) {
$this->logger->error('Plugin-Update Fehler', [
'plugin_name' => $name,
'error' => $e->getMessage()
]);
return false;
}
}
/**
* Plugin-Klasse laden
*/
private function loadPluginClass($name)
{
$pluginPath = $this->plugins[$name]['path'];
$mainFile = $pluginPath . '/Plugin.php';
if (!file_exists($mainFile)) {
return false;
}
require_once $mainFile;
$className = ucfirst($name) . 'Plugin';
if (!class_exists($className)) {
return false;
}
return $className;
}
/**
* Dependencies prüfen
*/
private function checkDependencies($dependencies)
{
foreach ($dependencies as $dependency) {
if (is_string($dependency)) {
// Plugin-Dependency
if (!isset($this->plugins[$dependency]) || !$this->plugins[$dependency]['active']) {
return false;
}
} elseif (is_array($dependency)) {
// Erweiterte Dependency-Prüfung
$type = $dependency['type'] ?? 'plugin';
$name = $dependency['name'] ?? '';
$version = $dependency['version'] ?? '';
switch ($type) {
case 'plugin':
if (!isset($this->plugins[$name]) || !$this->plugins[$name]['active']) {
return false;
}
if ($version && version_compare($this->plugins[$name]['version'], $version, '<')) {
return false;
}
break;
case 'php':
if (version_compare(PHP_VERSION, $version, '<')) {
return false;
}
break;
case 'extension':
if (!extension_loaded($name)) {
return false;
}
break;
}
}
}
return true;
}
/**
* Plugin-Hooks registrieren
*/
private function registerPluginHooks($pluginName, $hooks)
{
foreach ($hooks as $hook) {
$hookName = $hook['name'] ?? '';
$callback = $hook['callback'] ?? '';
$priority = $hook['priority'] ?? 10;
if ($hookName && $callback) {
$this->eventDispatcher->addListener($hookName, function($event) use ($pluginName, $callback) {
return $this->executePluginCallback($pluginName, $callback, $event);
}, $priority, $pluginName);
}
}
}
/**
* Plugin-Hooks deregistrieren
*/
private function unregisterPluginHooks($pluginName)
{
// Hooks für dieses Plugin entfernen
// (Implementierung hängt vom Event-System ab)
}
/**
* Plugin-Callback ausführen
*/
private function executePluginCallback($pluginName, $callback, $event)
{
if (!isset($this->plugins[$pluginName]['instance'])) {
return $event;
}
$instance = $this->plugins[$pluginName]['instance'];
if (method_exists($instance, $callback)) {
try {
return $instance->$callback($event);
} catch (\Exception $e) {
$this->logger->error('Plugin-Callback Fehler', [
'plugin_name' => $pluginName,
'callback' => $callback,
'error' => $e->getMessage()
]);
}
}
return $event;
}
/**
* Plugin in Datenbank speichern
*/
private function savePluginToDatabase($name, $plugin)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
INSERT INTO ws_plugins (
plugin_name, plugin_config, version, dependencies,
hooks, settings, active, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
plugin_config = ?, version = ?, dependencies = ?,
hooks = ?, settings = ?, updated_at = NOW()
');
$config = json_encode($plugin);
$dependencies = json_encode($plugin['dependencies']);
$hooks = json_encode($plugin['hooks']);
$settings = json_encode($plugin['settings']);
$stmt->execute([
$name,
$config,
$plugin['version'],
$dependencies,
$hooks,
$settings,
$plugin['active'] ? 1 : 0,
$config,
$plugin['version'],
$dependencies,
$hooks,
$settings
]);
} catch (Exception $e) {
$this->logger->error('Plugin-Datenbank Fehler', [
'error' => $e->getMessage()
]);
}
}
/**
* Plugin-Status in Datenbank aktualisieren
*/
private function updatePluginStatus($name, $active)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
UPDATE ws_plugins
SET active = ?, updated_at = NOW()
WHERE plugin_name = ?
');
$stmt->execute([$active ? 1 : 0, $name]);
} catch (Exception $e) {
$this->logger->error('Plugin-Status Update Fehler', [
'error' => $e->getMessage()
]);
}
}
/**
* Plugin-Version in Datenbank aktualisieren
*/
private function updatePluginVersion($name, $version)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
UPDATE ws_plugins
SET version = ?, updated_at = NOW()
WHERE plugin_name = ?
');
$stmt->execute([$version, $name]);
} catch (Exception $e) {
$this->logger->error('Plugin-Version Update Fehler', [
'error' => $e->getMessage()
]);
}
}
/**
* Plugin aus Datenbank löschen
*/
private function deletePluginFromDatabase($name)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
DELETE FROM ws_plugins
WHERE plugin_name = ?
');
$stmt->execute([$name]);
} catch (Exception $e) {
$this->logger->error('Plugin-Datenbank Löschung Fehler', [
'error' => $e->getMessage()
]);
}
}
/**
* Verzeichnis rekursiv löschen
*/
private function removeDirectory($dir)
{
if (!is_dir($dir)) {
return false;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (is_dir($path)) {
$this->removeDirectory($path);
} else {
unlink($path);
}
}
return rmdir($dir);
}
/**
* Alle Plugins abrufen
*/
public function getAllPlugins()
{
return $this->plugins;
}
/**
* Aktive Plugins abrufen
*/
public function getActivePlugins()
{
return array_filter($this->plugins, function($plugin) {
return $plugin['active'];
});
}
/**
* Plugin abrufen
*/
public function getPlugin($name)
{
return isset($this->plugins[$name]) ? $this->plugins[$name] : null;
}
/**
* Plugin-Statistiken abrufen
*/
public function getPluginStatistics()
{
$total = count($this->plugins);
$active = count($this->getActivePlugins());
$inactive = $total - $active;
$versions = [];
foreach ($this->plugins as $plugin) {
$version = $plugin['version'];
$versions[$version] = ($versions[$version] ?? 0) + 1;
}
return [
'total' => $total,
'active' => $active,
'inactive' => $inactive,
'versions' => $versions
];
}
/**
* Plugin-System aktivieren/deaktivieren
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Plugin-System Status prüfen
*/
public function isEnabled()
{
return $this->enabled;
}
}
/**
* Basis-Plugin-Klasse
*/
abstract class BasePlugin
{
protected $name;
protected $version;
protected $description;
protected $author;
protected $config;
public function __construct()
{
$this->loadConfig();
}
/**
* Plugin aktivieren
*/
abstract public function activate();
/**
* Plugin deaktivieren
*/
abstract public function deactivate();
/**
* Plugin-Konfiguration laden
*/
protected function loadConfig()
{
$configFile = dirname((new \ReflectionClass($this))->getFileName()) . '/plugin.json';
if (file_exists($configFile)) {
$this->config = json_decode(file_get_contents($configFile), true);
$this->name = $this->config['name'] ?? '';
$this->version = $this->config['version'] ?? '1.0.0';
$this->description = $this->config['description'] ?? '';
$this->author = $this->config['author'] ?? '';
}
}
/**
* Plugin-Konfiguration abrufen
*/
public function getConfig($key = null)
{
if ($key === null) {
return $this->config;
}
return $this->config[$key] ?? null;
}
/**
* Plugin-Konfiguration setzen
*/
public function setConfig($key, $value)
{
$this->config[$key] = $value;
return $this;
}
/**
* Plugin-Name abrufen
*/
public function getName()
{
return $this->name;
}
/**
* Plugin-Version abrufen
*/
public function getVersion()
{
return $this->version;
}
/**
* Plugin-Beschreibung abrufen
*/
public function getDescription()
{
return $this->description;
}
/**
* Plugin-Author abrufen
*/
public function getAuthor()
{
return $this->author;
}
}