Automatische Migration mit Rector

Rector ist ein PHP-Refactoring-Tool, das automatisch Code transformieren kann. REDAXO 6 verwendet Rector, um die Migration von Add-ons von REDAXO 5.x auf 6.x zu automatisieren.

Installation

1. Rector als Dev-Dependency hinzufügen

composer require --dev rector/rector

2. Rector-Konfiguration erstellen

Erstelle eine rector.php Datei im Wurzelverzeichnis deines Add-ons:

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Renaming\ValueObject\RenameStaticMethod;
use Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector;
use Rector\Transform\ValueObject\FuncCallToStaticCall;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/lib',
        __DIR__ . '/pages',
        __DIR__ . '/boot.php',
        __DIR__ . '/install.php',
        __DIR__ . '/uninstall.php',
        __DIR__ . '/update.php',
    ])
    ->withImportNames()

    // Klassen umbenennen
    ->withConfiguredRule(RenameClassRector::class, [
        // Core-Klassen
        'rex' => 'Redaxo\\Core\\Core',
        'rex_addon' => 'Redaxo\\Core\\Addon\\Addon',
        'rex_sql' => 'Redaxo\\Core\\Database\\Sql',
        'rex_sql_table' => 'Redaxo\\Core\\Database\\Table',
        'rex_sql_column' => 'Redaxo\\Core\\Database\\Column',
        'rex_article' => 'Redaxo\\Core\\Content\\Article',
        'rex_category' => 'Redaxo\\Core\\Content\\Category',
        'rex_clang' => 'Redaxo\\Core\\Language\\Language',
        'rex_user' => 'Redaxo\\Core\\Security\\User',
        'rex_form' => 'Redaxo\\Core\\Form\\Form',
        'rex_config_form' => 'Redaxo\\Core\\Form\\ConfigForm',
        'rex_list' => 'Redaxo\\Core\\View\\DataList',
        'rex_fragment' => 'Redaxo\\Core\\View\\Fragment',
        'rex_view' => 'Redaxo\\Core\\View\\View',
        'rex_path' => 'Redaxo\\Core\\Filesystem\\Path',
        'rex_url' => 'Redaxo\\Core\\Filesystem\\Url',
        'rex_file' => 'Redaxo\\Core\\Filesystem\\File',
        'rex_dir' => 'Redaxo\\Core\\Filesystem\\Dir',
        'rex_request' => 'Redaxo\\Core\\Http\\Request',
        'rex_response' => 'Redaxo\\Core\\Http\\Response',
        'rex_csrf_token' => 'Redaxo\\Core\\Security\\CsrfToken',
        'rex_logger' => 'Redaxo\\Core\\Log\\Logger',
        'rex_i18n' => 'Redaxo\\Core\\Translation\\I18n',
        'rex_extension' => 'Redaxo\\Core\\ExtensionPoint\\Extension',
        'rex_extension_point' => 'Redaxo\\Core\\ExtensionPoint\\ExtensionPoint',
        'rex_media' => 'Redaxo\\Core\\MediaPool\\Media',
        'rex_media_category' => 'Redaxo\\Core\\MediaPool\\MediaCategory',
        'rex_exception' => 'Redaxo\\Core\\Exception\\Exception',
        'rex_validator' => 'Redaxo\\Core\\Validator\\Validator',
        'rex_string' => 'Redaxo\\Core\\Util\\Str',
        'rex_formatter' => 'Redaxo\\Core\\Util\\Formatter',
        'rex_mailer' => 'Redaxo\\Core\\Mailer\\Mailer',
        
        // Weitere Klassen nach Bedarf hinzufügen
        // Vollständige Liste: siehe migration-klassen.md
    ])

    // Globale Funktionen zu statischen Methoden
    ->withConfiguredRule(FuncCallToStaticCallRector::class, [
        new FuncCallToStaticCall('rex_get', 'Redaxo\\Core\\Http\\Request', 'get'),
        new FuncCallToStaticCall('rex_post', 'Redaxo\\Core\\Http\\Request', 'post'),
        new FuncCallToStaticCall('rex_request', 'Redaxo\\Core\\Http\\Request', 'request'),
        new FuncCallToStaticCall('rex_cookie', 'Redaxo\\Core\\Http\\Request', 'cookie'),
        new FuncCallToStaticCall('rex_session', 'Redaxo\\Core\\Http\\Request', 'session'),
        new FuncCallToStaticCall('rex_set_session', 'Redaxo\\Core\\Http\\Request', 'setSession'),
        new FuncCallToStaticCall('rex_unset_session', 'Redaxo\\Core\\Http\\Request', 'unsetSession'),
        new FuncCallToStaticCall('rex_server', 'Redaxo\\Core\\Http\\Request', 'server'),
        new FuncCallToStaticCall('rex_getUrl', 'Redaxo\\Core\\Filesystem\\Url', 'article'),
        new FuncCallToStaticCall('rex_delete_cache', 'Redaxo\\Core\\Cache', 'delete'),
    ])

    // Methoden umbenennen
    ->withConfiguredRule(RenameMethodRector::class, [
        new MethodCallRename('Redaxo\\Core\\Content\\Article', 'getClang', 'getClangId'),
        new MethodCallRename('Redaxo\\Core\\Content\\Category', 'getClang', 'getClangId'),
        new MethodCallRename('Redaxo\\Core\\Content\\ArticleSlice', 'getClang', 'getClangId'),
        new MethodCallRename('Redaxo\\Core\\Addon\\Addon', 'getRegisteredPackages', 'getRegisteredAddons'),
        new MethodCallRename('Redaxo\\Core\\Addon\\Addon', 'getAvailablePackages', 'getAvailableAddons'),
    ])

    // Statische Methoden verschieben
    ->withConfiguredRule(RenameStaticMethodRector::class, [
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'addCssFile', 'Redaxo\\Core\\View\\Asset', 'addCssFile'),
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'addJsFile', 'Redaxo\\Core\\View\\Asset', 'addJsFile'),
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'error', 'Redaxo\\Core\\View\\Message', 'error'),
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'success', 'Redaxo\\Core\\View\\Message', 'success'),
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'info', 'Redaxo\\Core\\View\\Message', 'info'),
        new RenameStaticMethod('Redaxo\\Core\\View\\View', 'warning', 'Redaxo\\Core\\View\\Message', 'warning'),
    ])
;

Rector ausführen

Vorschau (Dry-Run)

Zuerst die Änderungen prüfen, ohne Dateien zu modifizieren:

vendor/bin/rector process --dry-run

Änderungen anwenden

vendor/bin/rector process

Vollständige REDAXO 6 Rector-Konfiguration

Die offizielle und vollständige Rector-Konfiguration findest du im REDAXO 6.x Branch:

Repository: github.com/redaxo/redaxo
Branch: 6.x
Datei: rector.php

Diese enthält alle Transformationsregeln für:

  • Über 200 Klassenumbenennungen
  • Methodenumbenennungen
  • Funktionen zu statischen Methoden
  • Parameter-Änderungen
  • Konstanten-Migration

Verwendung der offiziellen Konfiguration

Du kannst die offizielle Konfiguration als Basis verwenden:

# REDAXO 6 Repository klonen
git clone --branch 6.x https://github.com/redaxo/redaxo.git redaxo6

# Rector-Konfiguration kopieren
cp redaxo6/rector.php ./rector.php

# Pfade in rector.php anpassen

Passe dann die ->withPaths([...]) Konfiguration an die Struktur deines Add-ons an.

Beispiel: Add-on Migration

Vorher (REDAXO 5.x)

<?php

class my_addon_helper
{
    public static function getData($articleId)
    {
        $clang = rex_clang::getCurrentId();
        $article = rex_article::get($articleId, $clang);
        
        if (!$article) {
            throw new rex_exception('Article not found');
        }
        
        $sql = rex_sql::factory();
        $sql->setTable(rex::getTable('mydata'));
        $sql->setWhere(['article_id' => $articleId]);
        $sql->select();
        
        return $sql->getArray();
    }
}

// Im Template/Modul
$id = rex_get('id', 'int', 0);
echo rex_view::success('Daten geladen');
rex_view::addCssFile(rex_addon::get('my_addon')->getAssetsUrl('style.css'));

Nachher (REDAXO 6.x)

<?php

use Redaxo\Core\Addon\Addon;
use Redaxo\Core\Content\Article;
use Redaxo\Core\Core;
use Redaxo\Core\Database\Sql;
use Redaxo\Core\Exception\Exception;
use Redaxo\Core\Http\Request;
use Redaxo\Core\Language\Language;
use Redaxo\Core\View\Asset;
use Redaxo\Core\View\Message;

class my_addon_helper
{
    public static function getData($articleId)
    {
        $clang = Language::getCurrentId();
        $article = Article::get($articleId, $clang);
        
        if (!$article) {
            throw new Exception('Article not found');
        }
        
        $sql = Sql::factory();
        $sql->setTable(Core::getTable('mydata'));
        $sql->setWhere(['article_id' => $articleId]);
        $sql->select();
        
        return $sql->getArray();
    }
}

// Im Template/Modul
$id = Request::get('id', 'int', 0);
echo Message::success('Daten geladen');
Asset::addCssFile(Addon::get('my_addon')->getAssetsUrl('style.css'));

Schrittweise Migration

Für größere Add-ons empfiehlt sich eine schrittweise Migration:

Schritt 1: Nur Klassen umbenennen

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/lib'])
    ->withConfiguredRule(RenameClassRector::class, [
        // Nur Kernklassen
        'rex' => 'Redaxo\\Core\\Core',
        'rex_sql' => 'Redaxo\\Core\\Database\\Sql',
        // ...
    ])
;

Schritt 2: Funktionen migrieren

    ->withConfiguredRule(FuncCallToStaticCallRector::class, [
        new FuncCallToStaticCall('rex_get', 'Redaxo\\Core\\Http\\Request', 'get'),
        // ...
    ])

Schritt 3: Methoden anpassen

    ->withConfiguredRule(RenameMethodRector::class, [
        new MethodCallRename('Redaxo\\Core\\Content\\Article', 'getClang', 'getClangId'),
        // ...
    ])

Tipps für die Migration

1. Backup erstellen

git checkout -b redaxo6-migration

2. Tests ausführen (falls vorhanden)

# Vor Migration
vendor/bin/phpunit

# Nach Migration
vendor/bin/phpunit

3. Manuelle Nacharbeit

Rector kann nicht alle Änderungen automatisch durchführen:

  • package.yml: requires auf redaxo: ^6.0 anpassen
  • Eigene Namespaces: PSR-4 Autoloading für eigene Klassen einrichten

    Lege deine eigenen Klassen z. B. im Verzeichnis src/ ab und richte in der composer.json deines Add-ons einen passenden PSR-4-Namespace ein:

    ```json { “autoload”: { “psr-4”: { “Vendor\\MeinAddon\\”: “src/” } } }

  • Entfernte Features: Code für entfernte Funktionen manuell anpassen

4. IDE-Unterstützung

Nach der Migration die IDE-Indizes aktualisieren:

  • PhpStorm: File → Invalidate Caches / Restart
  • VS Code: PHP Intelephense neu starten

Häufige Probleme

Problem: Klasse nicht gefunden

Class 'rex' not found

Lösung: Use-Statement hinzufügen:

use Redaxo\Core\Core;

Problem: Methode existiert nicht

Call to undefined method getClang()

Lösung: Methode wurde umbenannt. Siehe migration-methoden.md

Problem: Funktion existiert nicht

Call to undefined function rex_get()

Lösung: Globale Funktion durch statische Methode ersetzen:

use Redaxo\Core\Http\Request;
$value = Request::get('key', 'string');

Weiterführende Dokumentation