17 Anonymous Classes

Anonyme Klassen wurden in PHP 7.0 eingeführt und bieten eine praktische Möglichkeit, Klassen zu definieren und zu instanziieren, ohne ihnen einen Namen zu geben. Diese Funktion ist besonders nützlich für einmalige Objekte, die nur an einer Stelle im Code verwendet werden.

17.1 Grundlagen der anonymen Klassen

Eine anonyme Klasse wird definiert und instanziiert in einem einzigen Schritt:

$objekt = new class {
    public function hallo(): string {
        return "Hallo von einer anonymen Klasse!";
    }
};

echo $objekt->hallo(); // Ausgabe: Hallo von einer anonymen Klasse!

Im Gegensatz zu benannten Klassen haben anonyme Klassen keinen Namen, auf den später im Code Bezug genommen werden kann. Sie werden direkt instanziiert, wenn sie definiert werden.

17.2 Verwendungszwecke für anonyme Klassen

Anonyme Klassen sind besonders nützlich in folgenden Situationen:

  1. Einmalige Implementierungen: Wenn eine Klasse nur an einer Stelle benötigt wird
  2. Callback-Funktionen: Als komplexere Alternative zu Closures
  3. Kurzlebige Objekte: Für temporäre, spezialisierte Objekte
  4. Testcode: Zum schnellen Erstellen von Mocks oder Stubs für Tests
  5. Einfache Implementierungen von Interfaces: Wenn eine vollständige benannte Klasse zu umständlich wäre

17.3 Konstruktorparameter und -argumente

Wie bei benannten Klassen können auch anonyme Klassen Konstruktoren mit Parametern haben:

$logger = new class('app.log') {
    private string $dateiname;
    
    public function __construct(string $dateiname) {
        $this->dateiname = $dateiname;
        echo "Logger initialisiert mit Datei: {$this->dateiname}\n";
    }
    
    public function log(string $nachricht): void {
        echo "Schreibe in {$this->dateiname}: $nachricht\n";
    }
};

$logger->log("Anwendung gestartet");

17.4 Vererbung und Interface-Implementierung

Anonyme Klassen können von anderen Klassen erben und Interfaces implementieren:

interface MessageHandler {
    public function handle(string $nachricht): void;
}

abstract class AbstractLogger {
    protected function formatNachricht(string $nachricht): string {
        return date('Y-m-d H:i:s') . ' - ' . $nachricht;
    }
}

$logger = new class extends AbstractLogger implements MessageHandler {
    public function handle(string $nachricht): void {
        $formattedMessage = $this->formatNachricht($nachricht);
        echo "Handler: $formattedMessage\n";
    }
};

$logger->handle("Ein wichtiges Ereignis");

17.5 Anonyme Klassen als Methodenrückgabewerte

Anonyme Klassen können als Rückgabewerte von Methoden verwendet werden, um spezialisierte Implementierungen zu erstellen:

class FormatterFactory {
    public static function createFormatter(string $typ): object {
        switch ($typ) {
            case 'json':
                return new class {
                    public function format(array $daten): string {
                        return json_encode($daten);
                    }
                };
            
            case 'xml':
                return new class {
                    public function format(array $daten): string {
                        $xml = new SimpleXMLElement('<root/>');
                        
                        foreach ($daten as $key => $value) {
                            $xml->addChild($key, $value);
                        }
                        
                        return $xml->asXML();
                    }
                };
            
            case 'csv':
                return new class {
                    public function format(array $daten): string {
                        $output = fopen('php://temp', 'r+');
                        fputcsv($output, array_keys($daten));
                        fputcsv($output, array_values($daten));
                        rewind($output);
                        
                        $csv = stream_get_contents($output);
                        fclose($output);
                        
                        return $csv;
                    }
                };
            
            default:
                throw new InvalidArgumentException("Unbekannter Formatierer: $typ");
        }
    }
}

$jsonFormatter = FormatterFactory::createFormatter('json');
$xmlFormatter = FormatterFactory::createFormatter('xml');

$daten = ['name' => 'Max Mustermann', 'alter' => 30];

echo $jsonFormatter->format($daten) . "\n";
echo $xmlFormatter->format($daten) . "\n";

17.6 Zugriff auf äußeren Scope

Anonyme Klassen können Zugriff auf Variablen aus dem umgebenden Scope haben, indem sie sie im Konstruktor übergeben:

$prefix = "LOG";
$suffix = "END";

$logger = new class($prefix, $suffix) {
    private string $prefix;
    private string $suffix;
    
    public function __construct(string $prefix, string $suffix) {
        $this->prefix = $prefix;
        $this->suffix = $suffix;
    }
    
    public function log(string $nachricht): void {
        echo "{$this->prefix}: $nachricht :{$this->suffix}\n";
    }
};

$logger->log("Wichtige Nachricht"); // Ausgabe: LOG: Wichtige Nachricht :END

17.7 Verwendung mit Traits

Anonyme Klassen können auch Traits verwenden:

trait Loggable {
    private string $logLevel = 'INFO';
    
    public function setLogLevel(string $level): void {
        $this->logLevel = $level;
    }
    
    public function log(string $nachricht): void {
        echo "[{$this->logLevel}] $nachricht\n";
    }
}

trait Timestampable {
    public function getTimestamp(): string {
        return date('Y-m-d H:i:s');
    }
}

$logger = new class {
    use Loggable, Timestampable;
    
    public function logWithTimestamp(string $nachricht): void {
        $this->log($this->getTimestamp() . " - " . $nachricht);
    }
};

$logger->setLogLevel('DEBUG');
$logger->logWithTimestamp("Anwendung gestartet");

17.8 Anonyme Klassen als Callbacks

Anonyme Klassen eignen sich gut als komplexe Callbacks, besonders wenn mehrere Methoden oder Zustände benötigt werden:

$daten = [5, 1, 9, 3, 7];

// Sortieren mit einer anonymen Klasse als Callback
usort($daten, new class {
    private bool $aufsteigend = true;
    
    public function __invoke(int $a, int $b): int {
        return $this->aufsteigend ? $a <=> $b : $b <=> $a;
    }
});

print_r($daten); // Ausgabe: [1, 3, 5, 7, 9]

17.9 Anonyme Klassen mit Type Hints

Anonyme Klassen funktionieren nahtlos mit Type Hints zusammen, indem sie Interfaces implementieren:

interface DataProcessor {
    public function process(array $daten): array;
}

function verarbeiteUndFiltere(DataProcessor $processor, array $daten): array {
    $verarbeitet = $processor->process($daten);
    return array_filter($verarbeitet, fn($item) => $item > 0);
}

$result = verarbeiteUndFiltere(
    new class implements DataProcessor {
        public function process(array $daten): array {
            return array_map(fn($item) => $item * 2 - 5, $daten);
        }
    },
    [1, 2, 3, 4, 5]
);

print_r($result); // Ausgabe: [1 => -1, 2 => 1, 3 => 3, 4 => 5]

17.10 Interne Implementierung und Performance

Intern werden anonyme Klassen mit einem automatisch generierten Namen versehen. Dieser Name wird für jeden anonymen Klassenblock im Quellcode einmalig generiert:

$class1 = new class {};
$class2 = new class {};

echo get_class($class1) . "\n"; // Ausgabe: class@anonymous/file.php:0x123456
echo get_class($class2) . "\n"; // Ausgabe: class@anonymous/file.php:0x789abc

Bezüglich der Performance gibt es keinen signifikanten Unterschied zwischen anonymen und benannten Klassen. Beide werden zur Laufzeit geladen und verarbeitet. Der Hauptunterschied liegt in der Lesbarkeit und Organisation des Codes.

17.11 Praktisches Beispiel: Event Listener

Ein typisches Anwendungsbeispiel für anonyme Klassen sind Event Listener in einem Event-System:

interface EventListener {
    public function handle(array $eventData): void;
}

class EventDispatcher {
    private array $listeners = [];
    
    public function addListener(string $event, EventListener $listener): void {
        $this->listeners[$event][] = $listener;
    }
    
    public function dispatch(string $event, array $data = []): void {
        if (!isset($this->listeners[$event])) {
            return;
        }
        
        foreach ($this->listeners[$event] as $listener) {
            $listener->handle($data);
        }
    }
}

// Verwendung des Event-Systems mit anonymen Klassen
$dispatcher = new EventDispatcher();

// Füge einen Logger-Listener hinzu
$dispatcher->addListener('user.login', new class implements EventListener {
    public function handle(array $eventData): void {
        echo "LOGIN: Benutzer {$eventData['username']} hat sich angemeldet\n";
    }
});

// Füge einen Statistik-Listener hinzu
$dispatcher->addListener('user.login', new class implements EventListener {
    private array $logins = [];
    
    public function handle(array $eventData): void {
        $this->logins[] = [
            'username' => $eventData['username'],
            'time' => time()
        ];
        
        echo "STATS: {$eventData['username']} ist der " . count($this->logins) . ". Login\n";
    }
});

// Dispatche ein Event
$dispatcher->dispatch('user.login', [
    'username' => 'max.mustermann',
    'ip' => '192.168.1.1'
]);

17.12 Praktisches Beispiel: Strategiemuster

Anonyme Klassen eignen sich hervorragend für die Implementierung des Strategiemusters, besonders wenn die Strategien nur an einer Stelle verwendet werden:

interface ZahlungsStrategie {
    public function bezahlen(float $betrag): bool;
}

class Bestellung {
    private array $produkte = [];
    private ?ZahlungsStrategie $zahlungsStrategie = null;
    
    public function addProdukt(string $name, float $preis, int $menge = 1): void {
        $this->produkte[] = [
            'name' => $name,
            'preis' => $preis,
            'menge' => $menge
        ];
    }
    
    public function setZahlungsStrategie(ZahlungsStrategie $strategie): void {
        $this->zahlungsStrategie = $strategie;
    }
    
    public function getGesamtbetrag(): float {
        $summe = 0;
        foreach ($this->produkte as $produkt) {
            $summe += $produkt['preis'] * $produkt['menge'];
        }
        return $summe;
    }
    
    public function bezahlen(): bool {
        if ($this->zahlungsStrategie === null) {
            throw new RuntimeException("Keine Zahlungsstrategie festgelegt");
        }
        
        $betrag = $this->getGesamtbetrag();
        return $this->zahlungsStrategie->bezahlen($betrag);
    }
}

// Verwendung
$bestellung = new Bestellung();
$bestellung->addProdukt("PHP-Buch", 29.99);
$bestellung->addProdukt("USB-Stick", 9.99, 2);

// Kreditkartenzahlung mit anonymer Klasse
$bestellung->setZahlungsStrategie(new class implements ZahlungsStrategie {
    private string $kartenNummer = '1234-5678-9012-3456';
    private string $kartenInhaber = 'Max Mustermann';
    
    public function bezahlen(float $betrag): bool {
        echo "Zahle {$betrag}€ mit Kreditkarte ({$this->kartenNummer})\n";
        // In einer realen Anwendung würde hier die Zahlungsabwicklung erfolgen
        return true;
    }
});

$bestellung->bezahlen();

// Alternative Zahlungsmethode: PayPal
$bestellung->setZahlungsStrategie(new class implements ZahlungsStrategie {
    private string $email = 'max.mustermann@example.com';
    
    public function bezahlen(float $betrag): bool {
        echo "Zahle {$betrag}€ mit PayPal ({$this->email})\n";
        // PayPal-Zahlungsabwicklung
        return true;
    }
});

$bestellung->bezahlen();

17.13 Vor- und Nachteile anonymer Klassen

17.13.1 Vorteile:

  1. Lokalität des Codes: Die Implementierung liegt direkt dort, wo sie verwendet wird
  2. Keine Namenskonflikte: Es müssen keine eindeutigen Klassennamen gefunden werden
  3. Reduzierung von Boilerplate-Code: Weniger Dateien und Deklarationen für einfache Anwendungsfälle
  4. Einmalige Implementierungen: Ideal für Klassen, die nur an einer Stelle verwendet werden
  5. Kapselung: Details, die nur in einem begrenzten Bereich relevant sind, bleiben dort

17.13.2 Nachteile:

  1. Eingeschränkte Wiederverwendbarkeit: Anonyme Klassen können nicht an anderen Stellen wiederverwendet werden
  2. Potenzielle Lesbarkeit: Sehr komplexe anonyme Klassen können die Lesbarkeit beeinträchtigen
  3. Debugging: Kann in manchen IDE-Umgebungen schwieriger zu debuggen sein
  4. Code-Organisation: Kann bei übermäßiger Verwendung zu unübersichtlichem Code führen
  5. Dokumentation: Schwieriger zu dokumentieren als benannte Klassen

17.14 Best Practices

Für die effektive Nutzung anonymer Klassen sollten folgende Best Practices beachtet werden:

  1. Einfach halten: Verwenden Sie anonyme Klassen für einfache, einmalige Implementierungen. Komplexere Klassen sollten benannt werden.

  2. Interface-Implementierung: Kombinieren Sie anonyme Klassen mit Interfaces, um klare Verträge zu definieren.

  3. Kleine Schnittstellen: Anonyme Klassen eignen sich besonders gut für Interfaces mit wenigen Methoden.

  4. Fokus auf spezifische Aufgaben: Jede anonyme Klasse sollte eine klar definierte, einzelne Verantwortung haben.

  5. Vermeiden von Duplizierung: Wenn Sie die gleiche anonyme Klasse an mehreren Stellen verwenden, ist es besser, eine benannte Klasse zu erstellen.