589 lines
17 KiB
PHP
589 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Copyright seit 2024 Webshop System
|
|
*
|
|
* Performance-Optimierung für PrestaShop-Modul-Kompatibilität
|
|
*
|
|
* @author Webshop System
|
|
* @license GPL v3
|
|
*/
|
|
|
|
namespace App\Core;
|
|
|
|
use Doctrine\DBAL\DriverManager;
|
|
use Doctrine\DBAL\Exception;
|
|
|
|
class PerformanceOptimizer
|
|
{
|
|
private static $instance = null;
|
|
private $cache;
|
|
private $logger;
|
|
private $enabled = true;
|
|
private $redisEnabled = false;
|
|
private $memcachedEnabled = false;
|
|
private $lazyLoadingEnabled = true;
|
|
private $databaseOptimizationEnabled = true;
|
|
private $memoryOptimizationEnabled = true;
|
|
|
|
private function __construct()
|
|
{
|
|
$this->cache = Cache::getInstance();
|
|
$this->logger = Logger::getInstance();
|
|
$this->loadSettings();
|
|
}
|
|
|
|
/**
|
|
* Singleton-Instanz abrufen
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Einstellungen laden
|
|
*/
|
|
private function loadSettings()
|
|
{
|
|
try {
|
|
$conn = DriverManager::getConnection([
|
|
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
|
|
]);
|
|
|
|
$stmt = $conn->prepare('
|
|
SELECT setting_key, setting_value
|
|
FROM ws_performance_settings
|
|
WHERE active = 1
|
|
');
|
|
$stmt->execute();
|
|
|
|
$settings = $stmt->fetchAllAssociative();
|
|
|
|
foreach ($settings as $setting) {
|
|
switch ($setting['setting_key']) {
|
|
case 'enabled':
|
|
$this->enabled = (bool)$setting['setting_value'];
|
|
break;
|
|
case 'redis_enabled':
|
|
$this->redisEnabled = (bool)$setting['setting_value'];
|
|
break;
|
|
case 'memcached_enabled':
|
|
$this->memcachedEnabled = (bool)$setting['setting_value'];
|
|
break;
|
|
case 'lazy_loading_enabled':
|
|
$this->lazyLoadingEnabled = (bool)$setting['setting_value'];
|
|
break;
|
|
case 'database_optimization_enabled':
|
|
$this->databaseOptimizationEnabled = (bool)$setting['setting_value'];
|
|
break;
|
|
case 'memory_optimization_enabled':
|
|
$this->memoryOptimizationEnabled = (bool)$setting['setting_value'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Performance-Einstellungen laden Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redis-Cache initialisieren
|
|
*/
|
|
public function initRedisCache()
|
|
{
|
|
if (!$this->redisEnabled) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$redis = new \Redis();
|
|
$redis->connect('127.0.0.1', 6379);
|
|
|
|
// Redis-Konfiguration
|
|
$redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
|
|
$redis->setOption(\Redis::OPT_PREFIX, 'webshop:');
|
|
|
|
return $redis;
|
|
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Redis-Verbindung Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Memcached-Cache initialisieren
|
|
*/
|
|
public function initMemcachedCache()
|
|
{
|
|
if (!$this->memcachedEnabled) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$memcached = new \Memcached();
|
|
$memcached->addServer('127.0.0.1', 11211);
|
|
|
|
return $memcached;
|
|
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Memcached-Verbindung Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lazy-Loading für Module
|
|
*/
|
|
public function lazyLoadModule($moduleName)
|
|
{
|
|
if (!$this->lazyLoadingEnabled) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$cacheKey = 'lazy_loaded_module_' . $moduleName;
|
|
|
|
// Cache prüfen
|
|
$cached = $this->cache->get($cacheKey);
|
|
if ($cached !== null) {
|
|
return $cached;
|
|
}
|
|
|
|
// Module-Klasse laden
|
|
$moduleClass = $this->loadModuleClass($moduleName);
|
|
|
|
if ($moduleClass) {
|
|
// Cache setzen
|
|
$this->cache->set($cacheKey, $moduleClass, 3600); // 1 Stunde
|
|
|
|
$this->logger->info('Module lazy geladen', [
|
|
'module_name' => $moduleName
|
|
]);
|
|
|
|
return $moduleClass;
|
|
}
|
|
|
|
return false;
|
|
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Lazy-Loading Fehler', [
|
|
'module_name' => $moduleName,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Module-Klasse laden
|
|
*/
|
|
private function loadModuleClass($moduleName)
|
|
{
|
|
$modulePath = __DIR__ . '/../../../modules/' . $moduleName . '/Module.php';
|
|
|
|
if (!file_exists($modulePath)) {
|
|
return false;
|
|
}
|
|
|
|
require_once $modulePath;
|
|
|
|
$className = ucfirst($moduleName) . 'Module';
|
|
|
|
if (!class_exists($className)) {
|
|
return false;
|
|
}
|
|
|
|
return $className;
|
|
}
|
|
|
|
/**
|
|
* Database-Optimierung
|
|
*/
|
|
public function optimizeDatabase()
|
|
{
|
|
if (!$this->databaseOptimizationEnabled) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$conn = DriverManager::getConnection([
|
|
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
|
|
]);
|
|
|
|
// Module-Tabellen optimieren
|
|
$tables = [
|
|
'ws_modules',
|
|
'ws_hooks',
|
|
'ws_overrides',
|
|
'ws_events',
|
|
'ws_cache',
|
|
'ws_logs',
|
|
'ws_plugins',
|
|
'ws_extensions',
|
|
'ws_dependencies'
|
|
];
|
|
|
|
foreach ($tables as $table) {
|
|
$stmt = $conn->prepare('OPTIMIZE TABLE ' . $table);
|
|
$stmt->execute();
|
|
}
|
|
|
|
// Indizes analysieren
|
|
foreach ($tables as $table) {
|
|
$stmt = $conn->prepare('ANALYZE TABLE ' . $table);
|
|
$stmt->execute();
|
|
}
|
|
|
|
$this->logger->info('Database-Optimierung abgeschlossen', [
|
|
'tables_optimized' => count($tables)
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Database-Optimierung Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Memory-Optimierung
|
|
*/
|
|
public function optimizeMemory()
|
|
{
|
|
if (!$this->memoryOptimizationEnabled) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Garbage Collection erzwingen
|
|
gc_collect_cycles();
|
|
|
|
// Memory-Limit prüfen
|
|
$memoryLimit = ini_get('memory_limit');
|
|
$memoryUsage = memory_get_usage(true);
|
|
$memoryPeak = memory_get_peak_usage(true);
|
|
|
|
// Cache optimieren
|
|
$this->optimizeCache();
|
|
|
|
// Unused Variables löschen
|
|
$this->cleanupUnusedVariables();
|
|
|
|
$this->logger->info('Memory-Optimierung abgeschlossen', [
|
|
'memory_limit' => $memoryLimit,
|
|
'memory_usage' => $this->formatBytes($memoryUsage),
|
|
'memory_peak' => $this->formatBytes($memoryPeak)
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Memory-Optimierung Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cache optimieren
|
|
*/
|
|
private function optimizeCache()
|
|
{
|
|
// Alte Cache-Einträge löschen
|
|
$this->cache->cleanup();
|
|
|
|
// Cache-Größe reduzieren
|
|
$this->cache->optimize();
|
|
}
|
|
|
|
/**
|
|
* Unused Variables löschen
|
|
*/
|
|
private function cleanupUnusedVariables()
|
|
{
|
|
// Globale Variablen prüfen
|
|
$globalVars = get_defined_vars();
|
|
|
|
foreach ($globalVars as $var => $value) {
|
|
if (is_object($value) && method_exists($value, '__destruct')) {
|
|
unset($globalVars[$var]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performance-Monitoring
|
|
*/
|
|
public function monitorPerformance()
|
|
{
|
|
$metrics = [
|
|
'memory_usage' => memory_get_usage(true),
|
|
'memory_peak' => memory_get_peak_usage(true),
|
|
'execution_time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
|
|
'database_queries' => $this->getDatabaseQueryCount(),
|
|
'cache_hits' => $this->cache->getHitCount(),
|
|
'cache_misses' => $this->cache->getMissCount()
|
|
];
|
|
|
|
// Metrics speichern
|
|
$this->savePerformanceMetrics($metrics);
|
|
|
|
// Alerts bei Performance-Problemen
|
|
$this->checkPerformanceAlerts($metrics);
|
|
|
|
return $metrics;
|
|
}
|
|
|
|
/**
|
|
* Database-Query-Count abrufen
|
|
*/
|
|
private function getDatabaseQueryCount()
|
|
{
|
|
// Vereinfachte Implementierung
|
|
// In der Praxis würde hier ein Query-Counter verwendet
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Performance-Metrics speichern
|
|
*/
|
|
private function savePerformanceMetrics($metrics)
|
|
{
|
|
try {
|
|
$conn = DriverManager::getConnection([
|
|
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
|
|
]);
|
|
|
|
$stmt = $conn->prepare('
|
|
INSERT INTO ws_performance_metrics (
|
|
memory_usage, memory_peak, execution_time,
|
|
database_queries, cache_hits, cache_misses, created_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, NOW())
|
|
');
|
|
|
|
$stmt->execute([
|
|
$metrics['memory_usage'],
|
|
$metrics['memory_peak'],
|
|
$metrics['execution_time'],
|
|
$metrics['database_queries'],
|
|
$metrics['cache_hits'],
|
|
$metrics['cache_misses']
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Performance-Metrics speichern Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performance-Alerts prüfen
|
|
*/
|
|
private function checkPerformanceAlerts($metrics)
|
|
{
|
|
$alerts = [];
|
|
|
|
// Memory-Alert
|
|
if ($metrics['memory_usage'] > 100 * 1024 * 1024) { // 100MB
|
|
$alerts[] = 'High memory usage: ' . $this->formatBytes($metrics['memory_usage']);
|
|
}
|
|
|
|
// Execution-Time-Alert
|
|
if ($metrics['execution_time'] > 5.0) { // 5 Sekunden
|
|
$alerts[] = 'Slow execution time: ' . round($metrics['execution_time'], 2) . 's';
|
|
}
|
|
|
|
// Cache-Alert
|
|
$cacheHitRate = $metrics['cache_hits'] / max(1, $metrics['cache_hits'] + $metrics['cache_misses']);
|
|
if ($cacheHitRate < 0.8) { // 80%
|
|
$alerts[] = 'Low cache hit rate: ' . round($cacheHitRate * 100, 1) . '%';
|
|
}
|
|
|
|
if (!empty($alerts)) {
|
|
$this->logger->warning('Performance-Alerts', [
|
|
'alerts' => $alerts
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bytes formatieren
|
|
*/
|
|
private function formatBytes($bytes)
|
|
{
|
|
$units = ['B', 'KB', 'MB', 'GB'];
|
|
|
|
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
|
$bytes /= 1024;
|
|
}
|
|
|
|
return round($bytes, 2) . ' ' . $units[$i];
|
|
}
|
|
|
|
/**
|
|
* Performance-Statistiken abrufen
|
|
*/
|
|
public function getPerformanceStatistics()
|
|
{
|
|
try {
|
|
$conn = DriverManager::getConnection([
|
|
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
|
|
]);
|
|
|
|
// Durchschnittliche Performance-Metrics
|
|
$stmt = $conn->prepare('
|
|
SELECT
|
|
AVG(memory_usage) as avg_memory_usage,
|
|
AVG(memory_peak) as avg_memory_peak,
|
|
AVG(execution_time) as avg_execution_time,
|
|
AVG(database_queries) as avg_database_queries,
|
|
AVG(cache_hits) as avg_cache_hits,
|
|
AVG(cache_misses) as avg_cache_misses,
|
|
COUNT(*) as total_requests
|
|
FROM ws_performance_metrics
|
|
WHERE created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
|
|
');
|
|
$stmt->execute();
|
|
|
|
$stats = $stmt->fetchAssociative();
|
|
|
|
// Cache-Hit-Rate berechnen
|
|
$totalCacheRequests = $stats['avg_cache_hits'] + $stats['avg_cache_misses'];
|
|
$cacheHitRate = $totalCacheRequests > 0 ? ($stats['avg_cache_hits'] / $totalCacheRequests) * 100 : 0;
|
|
|
|
return [
|
|
'avg_memory_usage' => $this->formatBytes($stats['avg_memory_usage'] ?? 0),
|
|
'avg_memory_peak' => $this->formatBytes($stats['avg_memory_peak'] ?? 0),
|
|
'avg_execution_time' => round($stats['avg_execution_time'] ?? 0, 3),
|
|
'avg_database_queries' => round($stats['avg_database_queries'] ?? 0, 1),
|
|
'cache_hit_rate' => round($cacheHitRate, 1),
|
|
'total_requests' => $stats['total_requests'] ?? 0
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Performance-Statistiken abrufen Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performance-Einstellungen speichern
|
|
*/
|
|
public function saveSettings($settings)
|
|
{
|
|
try {
|
|
$conn = DriverManager::getConnection([
|
|
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
|
|
]);
|
|
|
|
foreach ($settings as $key => $value) {
|
|
$stmt = $conn->prepare('
|
|
INSERT INTO ws_performance_settings (
|
|
setting_key, setting_value, active, updated_at
|
|
) VALUES (?, ?, 1, NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
setting_value = ?, updated_at = NOW()
|
|
');
|
|
|
|
$stmt->execute([$key, $value, $value]);
|
|
}
|
|
|
|
// Einstellungen neu laden
|
|
$this->loadSettings();
|
|
|
|
$this->logger->info('Performance-Einstellungen gespeichert', [
|
|
'settings' => $settings
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Performance-Einstellungen speichern Fehler', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performance-Optimizer aktivieren/deaktivieren
|
|
*/
|
|
public function setEnabled($enabled)
|
|
{
|
|
$this->enabled = $enabled;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Performance-Optimizer Status prüfen
|
|
*/
|
|
public function isEnabled()
|
|
{
|
|
return $this->enabled;
|
|
}
|
|
|
|
/**
|
|
* Redis-Cache Status prüfen
|
|
*/
|
|
public function isRedisEnabled()
|
|
{
|
|
return $this->redisEnabled;
|
|
}
|
|
|
|
/**
|
|
* Memcached-Cache Status prüfen
|
|
*/
|
|
public function isMemcachedEnabled()
|
|
{
|
|
return $this->memcachedEnabled;
|
|
}
|
|
|
|
/**
|
|
* Lazy-Loading Status prüfen
|
|
*/
|
|
public function isLazyLoadingEnabled()
|
|
{
|
|
return $this->lazyLoadingEnabled;
|
|
}
|
|
|
|
/**
|
|
* Database-Optimierung Status prüfen
|
|
*/
|
|
public function isDatabaseOptimizationEnabled()
|
|
{
|
|
return $this->databaseOptimizationEnabled;
|
|
}
|
|
|
|
/**
|
|
* Memory-Optimierung Status prüfen
|
|
*/
|
|
public function isMemoryOptimizationEnabled()
|
|
{
|
|
return $this->memoryOptimizationEnabled;
|
|
}
|
|
}
|