Package Manager

Keywords: Package Addon Plugin Install Uninstall Activate Manager Dependency Requirements

Übersicht

Package-Manager für Installation, Deinstallation, Aktivierung und Dependency-Management von Addons/Plugins.

Methoden

rex_package_manager (Abstract Base)

Methode Parameter Rückgabe Beschreibung
::factory($package) rex_package rex_addon_manager|rex_plugin_manager Factory (erkennt Addon/Plugin)
install($installDump) bool bool Installiert Package (install.php + install.sql)
uninstall($installDump) bool bool Deinstalliert Package (uninstall.php + uninstall.sql)
activate() - bool Aktiviert Package (prüft Requirements)
deactivate() - bool Deaktiviert Package (prüft Dependencies)
delete() - bool Löscht Package-Dateien (deinstalliert vorher)
getMessage() - string Letzte Statusmeldung (Erfolg/Fehler)
checkRequirements() - bool Prüft Requirements (REDAXO-Version, Packages, PHP-Extensions)
checkDependencies() - bool Prüft ob andere Packages abhängig sind
checkConflicts() - bool Prüft Conflicts (incompatible Packages)
::generatePackageOrder() - void Generiert package.order.cache.inc
::synchronizeWithFileSystem() - void Scannt Filesystem nach neuen Packages
::saveConfig() - void Speichert config.yml aller Packages

rex_addon_manager

Extends rex_package_manager für Addons - keine zusätzlichen Methoden.

rex_plugin_manager

Extends rex_package_manager für Plugins - prüft zusätzlich ob Addon verfügbar.

Praxisbeispiele

Package installieren

// Addon installieren
$addon = rex_addon::get('yform');
$manager = rex_package_manager::factory($addon);

if ($manager->install()) {
    echo $manager->getMessage(); // "Addon 'YForm' erfolgreich installiert"
} else {
    echo $manager->getMessage(); // Fehlermeldung
}

// Plugin installieren
$plugin = rex_plugin::get('yform', 'manager');
$manager = rex_package_manager::factory($plugin);

if ($manager->install()) {
    echo 'Plugin installiert';
} else {
    echo 'Fehler: ' . $manager->getMessage();
}

Install ohne SQL-Dump

// Installation ohne install.sql Import
$addon = rex_addon::get('myAddon');
$manager = rex_package_manager::factory($addon);

// $installDump = false
if ($manager->install(false)) {
    echo 'Installiert ohne SQL-Import';
}

// Nützlich wenn:
// - SQL manuell importiert wird
// - Nur PHP-Code ausgeführt werden soll
// - Custom Setup über install.php

Package deinstallieren

$addon = rex_addon::get('debug');
$manager = rex_package_manager::factory($addon);

if ($manager->uninstall()) {
    echo $manager->getMessage();
} else {
    echo 'Fehler: ' . $manager->getMessage();
}

// Schritte bei Uninstall:
// 1. Deaktivieren (falls aktiv)
// 2. uninstall.php ausführen
// 3. uninstall.sql importieren
// 4. Assets löschen
// 5. Cache löschen
// 6. rex_config-Namespace löschen

Package aktivieren

$addon = rex_addon::get('cronjob');
$manager = rex_package_manager::factory($addon);

if ($manager->activate()) {
    echo 'Aktiviert';
} else {
    echo 'Fehler: ' . $manager->getMessage();
    // z.B. "Requirements nicht erfüllt"
}

// Prüft vor Aktivierung:
// - Package ist installiert
// - Requirements erfüllt (REDAXO-Version, Dependencies, PHP-Extensions)
// - Keine Conflicts

Package deaktivieren

$addon = rex_addon::get('debug');
$manager = rex_package_manager::factory($addon);

if ($manager->deactivate()) {
    echo 'Deaktiviert';
} else {
    echo 'Fehler: ' . $manager->getMessage();
    // z.B. "Andere Packages sind abhängig"
}

// Prüft Dependencies:
// - Kein anderes Package benötigt dieses
// - Löscht Cache
// - Aktualisiert package.order

Package löschen

$addon = rex_addon::get('old_addon');
$manager = rex_package_manager::factory($addon);

if ($manager->delete()) {
    echo 'Gelöscht';
} else {
    echo 'Fehler: ' . $manager->getMessage();
}

// Workflow:
// 1. Falls installiert: Uninstall
// 2. Löscht komplettes Package-Verzeichnis
// 3. synchronizeWithFileSystem()
// 4. saveConfig()

Requirements prüfen

$addon = rex_addon::get('myAddon');
$manager = rex_package_manager::factory($addon);

if ($manager->checkRequirements()) {
    echo 'Requirements erfüllt';
} else {
    echo $manager->getMessage();
    // z.B. "REDAXO Version 5.15 erforderlich"
    // oder "PHP Extension 'gd' fehlt"
    // oder "Addon 'phpmailer' nicht installiert"
}

// Prüft (aus package.yml):
// - redaxo: '^5.15'
// - php: { version: '^8.0', extensions: ['gd', 'intl'] }
// - packages: { phpmailer: '^3.0', yform: '^4.0' }

Dependencies prüfen

$addon = rex_addon::get('phpmailer');
$manager = rex_package_manager::factory($addon);

if ($manager->checkDependencies()) {
    echo 'Kann deaktiviert werden';
} else {
    echo $manager->getMessage();
    // "Addon 'yform' ist abhängig von 'phpmailer'"
}

// Prüft ob andere Packages dieses Package in 'requires' haben
// Verhindert Deaktivierung wenn Dependencies existieren

Conflicts prüfen

$addon = rex_addon::get('my_new_addon');
$manager = rex_package_manager::factory($addon);

if ($manager->checkConflicts()) {
    echo 'Keine Conflicts';
} else {
    echo $manager->getMessage();
    // "Conflict mit Addon 'old_addon'"
}

// Prüft (aus package.yml):
// conflicts:
//     old_addon: '*'
//     competitor_addon: '>=2.0'

Installation mit Error-Handling

$addon = rex_addon::get('complex_addon');
$manager = rex_package_manager::factory($addon);

try {
    if ($manager->install()) {
        echo rex_view::success($manager->getMessage());
        
        // Post-Install Tasks
        rex_delete_cache();
        rex_addon::get('complex_addon')->setConfig('installed_at', time());
        
    } else {
        echo rex_view::error($manager->getMessage());
        
        // Log Fehler
        rex_logger::factory()->error('Installation failed', [
            'addon' => $addon->getName(),
            'error' => $manager->getMessage()
        ]);
    }
} catch (Exception $e) {
    echo rex_view::error('Exception: ' . $e->getMessage());
}

Batch-Installation

// Mehrere Addons installieren
$addons = ['phpmailer', 'yform', 'cronjob', 'yrewrite'];
$failed = [];

foreach ($addons as $addonName) {
    $addon = rex_addon::get($addonName);
    $manager = rex_package_manager::factory($addon);
    
    if (!$manager->install()) {
        $failed[$addonName] = $manager->getMessage();
    }
}

if ($failed) {
    echo 'Fehler bei: ' . implode(', ', array_keys($failed));
} else {
    echo 'Alle Addons installiert';
}

Update-Script ausführen

// Nach Addon-Update: Update-Script manuell triggern
$addon = rex_addon::get('yform');

// Update-Script existiert?
$updateScript = $addon->getPath('update.php');
if (file_exists($updateScript)) {
    // Backup current version
    $oldVersion = $addon->getVersion();
    
    // Execute update
    $addon->includeFile('update.php');
    
    // Check for errors
    if ($updateMsg = $addon->getProperty('updatemsg')) {
        echo 'Update-Fehler: ' . $updateMsg;
    } else {
        echo 'Updated von ' . $oldVersion . ' zu ' . $addon->getVersion();
    }
}

Package-Order generieren

// package.order.cache.inc neu generieren
rex_package_manager::generatePackageOrder();

// Erstellt Lade-Reihenfolge basierend auf Dependencies
// Wichtig nach:
// - Installation/Deinstallation
// - Aktivierung/Deaktivierung
// - Manuellen package.yml Änderungen

Filesystem synchronisieren

// Neue Addons im Filesystem erkennen
rex_package_manager::synchronizeWithFileSystem();

// Scannt:
// - redaxo/src/addons/
// - redaxo/src/addons/*/plugins/
// 
// Registriert neue Packages in $REX['ADDON']
// Wird automatisch aufgerufen nach delete()

Config speichern

// Alle package.yml schreiben
rex_package_manager::saveConfig();

// Schreibt für jedes Package:
// - status (true/false)
// - install (true/false)
// - version
// 
// In: redaxo/data/core/config.yml

Installation mit Custom Requirements

// In install.php: Eigene Requirements prüfen
// Vor Installation durch Manager

$addon = rex_addon::get('myAddon');

// Mindestens PHP 8.1?
if (version_compare(PHP_VERSION, '8.1', '<')) {
    $addon->setProperty('installmsg', 'PHP 8.1 oder höher erforderlich');
    return; // Stoppt Installation
}

// MySQL 5.7+?
$sql = rex_sql::factory();
$version = $sql->getDbVersion();
if (version_compare($version, '5.7', '<')) {
    $addon->setProperty('installmsg', 'MySQL 5.7+ erforderlich');
    return;
}

// Custom Check
if (!function_exists('imagick')) {
    $addon->setProperty('installmsg', 'ImageMagick PHP-Extension fehlt');
    return;
}

// Success-Message setzen (optional)
$addon->setProperty('successmsg', 'Bitte API-Key in den Einstellungen konfigurieren');

Uninstall mit Cleanup

// In uninstall.php: Gründliches Cleanup

$addon = rex_addon::get('myAddon');

// 1. Config löschen (wird automatisch gemacht)
// rex_config::removeNamespace('myAddon');

// 2. Custom Dateien löschen
rex_dir::delete(rex_path::addonData('myAddon', 'uploads/'));
rex_file::delete(rex_path::addonData('myAddon', 'cache.json'));

// 3. Cronjobs löschen (wenn Cronjob-Addon vorhanden)
if (rex_addon::get('cronjob')->isAvailable()) {
    $sql = rex_sql::factory();
    $sql->setQuery('DELETE FROM rex_cronjob WHERE environment = ?', ['myAddon']);
}

// 4. YForm-Tables löschen (wenn YForm vorhanden)
if (rex_addon::get('yform')->isAvailable()) {
    rex_yform_manager_table::deleteTable('rex_myAddon_items');
}

// 5. Media-Kategorie löschen
$sql = rex_sql::factory();
$sql->setQuery('DELETE FROM rex_media_category WHERE name = ?', ['myAddon']);

// Error setzen wenn nötig
if ($someError) {
    $addon->setProperty('installmsg', 'Fehler beim Cleanup');
}

Plugin-Installation mit Addon-Check

// Plugin installieren - prüft automatisch ob Addon verfügbar

$plugin = rex_plugin::get('yform', 'manager');
$manager = rex_package_manager::factory($plugin);

// Wirft Fehler wenn Addon nicht verfügbar:
// "Addon 'YForm' muss installiert und aktiviert sein"
if ($manager->install()) {
    echo 'Plugin installiert';
}

Reinstallation

// Package reinstallieren (Uninstall + Install)

$addon = rex_addon::get('myAddon');
$manager = rex_package_manager::factory($addon);

// 1. Uninstall
if (!$manager->uninstall()) {
    echo 'Uninstall fehlgeschlagen: ' . $manager->getMessage();
    exit;
}

// 2. Install
if (!$manager->install()) {
    echo 'Install fehlgeschlagen: ' . $manager->getMessage();
    exit;
}

echo 'Reinstallation erfolgreich';

Installation in Extension Point

// Package programmatisch nach anderem Package installieren

rex_extension::register('PACKAGES_INCLUDED', function() {
    // Wenn 'phpmailer' verfügbar, installiere 'myMailer'
    if (rex_addon::get('phpmailer')->isAvailable()) {
        $myMailer = rex_addon::get('myMailer');
        
        if (!$myMailer->isInstalled()) {
            $manager = rex_package_manager::factory($myMailer);
            $manager->install();
        }
    }
});

Setup-Assistant

// Multi-Step Setup-Prozess

class myAddon_setup {
    public static function install() {
        $addon = rex_addon::get('myAddon');
        $manager = rex_package_manager::factory($addon);
        
        // Step 1: Install
        if (!$manager->install()) {
            return ['success' => false, 'message' => $manager->getMessage()];
        }
        
        // Step 2: Default Config
        $addon->setConfig('api_key', '');
        $addon->setConfig('mode', 'test');
        
        // Step 3: Create Directories
        rex_dir::create($addon->getDataPath('uploads'));
        rex_dir::create($addon->getDataPath('cache'));
        
        // Step 4: Import Demo Data (optional)
        if ($_POST['import_demo']) {
            $sql = rex_sql::factory();
            $sql->setQuery('INSERT INTO rex_myAddon_items ...');
        }
        
        return ['success' => true, 'message' => 'Setup abgeschlossen'];
    }
}

Upgrade-Migration

// In update.php: Migration von alter zu neuer Version

$addon = rex_addon::get('myAddon');
$currentVersion = $addon->getVersion();
$savedVersion = $addon->getConfig('version', '1.0.0');

// Von 1.x zu 2.x
if (version_compare($savedVersion, '2.0.0', '<')) {
    // DB-Schema ändern
    $sql = rex_sql::factory();
    $sql->setQuery('ALTER TABLE rex_myAddon_items ADD COLUMN new_field VARCHAR(255)');
    
    // Config migrieren
    $oldValue = $addon->getConfig('old_setting');
    $addon->setConfig('new_setting', $oldValue);
    $addon->removeConfig('old_setting');
    
    // Daten migrieren
    rex_sql::factory()->setQuery('UPDATE rex_myAddon_items SET new_field = old_field');
}

// Von 2.0.x zu 2.1.x
if (version_compare($savedVersion, '2.1.0', '<')) {
    // Kleinere Änderungen
    $addon->setConfig('feature_enabled', true);
}

// Version speichern
$addon->setConfig('version', $currentVersion);

System-Package-Schutz

// System-Packages können nicht gelöscht werden

$backup = rex_addon::get('backup');

if ($backup->isSystemPackage()) {
    echo 'System-Package - Löschen nicht erlaubt';
}

$manager = rex_package_manager::factory($backup);
if (!$manager->delete()) {
    echo $manager->getMessage(); // "Systempackage darf nicht gelöscht werden"
}

// System-Packages (aus package.yml):
// system_package: true

Package-Status-Report

// Status aller Packages

$packages = rex_package::getAvailablePackages();
$report = [];

foreach ($packages as $package) {
    $manager = rex_package_manager::factory($package);
    
    $report[] = [
        'id' => $package->getPackageId(),
        'name' => $package->getName(),
        'version' => $package->getVersion(),
        'installed' => $package->isInstalled(),
        'available' => $package->isAvailable(),
        'system' => $package->isSystemPackage(),
        'requirements_ok' => $manager->checkRequirements(),
        'conflicts' => !$manager->checkConflicts()
    ];
}

// JSON ausgeben für Monitoring
echo json_encode($report, JSON_PRETTY_PRINT);

Dependency-Graph

// Package-Dependencies visualisieren

function getPackageDependencies($packageId) {
    $package = rex_package::get($packageId);
    $requires = $package->getProperty('requires', []);
    
    $deps = [];
    if (isset($requires['packages'])) {
        foreach ($requires['packages'] as $depId => $version) {
            $deps[$depId] = [
                'version' => $version,
                'installed' => rex_package::get($depId)->isAvailable(),
                'dependencies' => getPackageDependencies($depId) // Recursive
            ];
        }
    }
    
    return $deps;
}

$deps = getPackageDependencies('yform');
print_r($deps);

// Output:
// [
//     'phpmailer' => [
//         'version' => '^3.0',
//         'installed' => true,
//         'dependencies' => []
//     ]
// ]

Installation Guard

// Sichere Installation mit Rollback bei Fehler

function safeInstall($addonName) {
    $addon = rex_addon::get($addonName);
    $manager = rex_package_manager::factory($addon);
    
    // Backup current state
    $wasInstalled = $addon->isInstalled();
    $wasActive = $addon->isAvailable();
    
    try {
        // Attempt install
        if (!$manager->install()) {
            throw new Exception($manager->getMessage());
        }
        
        // Verify installation
        if (!$addon->isInstalled()) {
            throw new Exception('Installation verification failed');
        }
        
        // Run smoke tests
        if (!runSmokeTests($addon)) {
            throw new Exception('Smoke tests failed');
        }
        
        return true;
        
    } catch (Exception $e) {
        // Rollback
        if (!$wasInstalled) {
            $manager->uninstall();
        }
        if (!$wasActive) {
            $manager->deactivate();
        }
        
        rex_logger::factory()->error('Installation failed: ' . $e->getMessage());
        return false;
    }
}

Asset-Management nach Installation

// Nach Installation: Assets verarbeiten

$addon = rex_addon::get('myAddon');

// Assets wurden kopiert von:
// redaxo/src/addons/myAddon/assets/
// nach:
// assets/addons/myAddon/

// SCSS kompilieren
if (file_exists($addon->getAssetsPath('styles.scss'))) {
    $compiler = new ScssPhp\ScssPhp\Compiler();
    $css = $compiler->compile(file_get_contents($addon->getAssetsPath('styles.scss')));
    rex_file::put($addon->getAssetsPath('styles.css'), $css);
}

// JS minifizieren
if (file_exists($addon->getAssetsPath('script.js'))) {
    $minifier = new MatthiasMullie\Minify\JS($addon->getAssetsPath('script.js'));
    $minifier->minify($addon->getAssetsPath('script.min.js'));
}

Aktivierung mit Late-Init

// Package aktivieren nachdem alle anderen Packages geladen sind

rex_extension::register('PACKAGES_INCLUDED', function() {
    $addon = rex_addon::get('late_init_addon');
    
    if ($addon->isInstalled() && !$addon->isAvailable()) {
        $manager = rex_package_manager::factory($addon);
        
        if ($manager->activate()) {
            rex_logger::factory()->info('Late activation successful');
        }
    }
}, rex_extension::LATE);