Joomla 1.0: Überraschend moderne Ideen unter 20 Jahren PHP

Das Component/Module/Template/Mambot-Modell dahinter war seiner Zeit architektonisch voraus.

Vor ein paar Tagen habe ich aus reiner Neugier das GitHub-Archiv von Joomla 1.0 geöffnet. Nicht das aktuelle Joomla – das wird seit Jahren aktiv weiterentwickelt und hat mit der Version von 2005 kaum noch etwas gemeinsam. Eigentlich wollte ich nur wissen, wie sich ein CMS anfühlt, das mich vor über zwanzig Jahren begeistert hat. Am Ende blieb ich deutlich länger hängen als geplant.

Die ursprüngliche Idee war sogar, ob sich das mit vertretbarem Aufwand auf PHP 8.3 heben ließe. Der Gedanke war schnell wieder verworfen. Aber der Blick in den Code hat sich trotzdem gelohnt – nicht wegen der Nostalgie, sondern wegen dem, was er über 20 Jahre PHP-Entwicklung erzählt.

Der erste Reflex war: Wo würde ich heute als Reviewer anfangen? Antwort: dort, wo jede Anfrage landet. Also habe ich vorne angefangen, beim Bootstrap.

Ein Blick in globals.php

2005, als Joomla 1.0 entstand, war register_globals bereits ein bekanntes Sicherheitsrisiko – der Default war seit PHP 4.2 (2002) auf Off gestellt, und sicherheitsbewusste Administratoren hatten es ohnehin deaktiviert. Trotzdem war die Landschaft uneinheitlich: Manche Hoster ließen es aus Kompatibilitätsgründen aktiviert, andere nicht. Ein CMS, das plattformübergreifend funktionieren sollte, konnte sich auf diese Einstellung nicht verlassen.

Joomla 1.0 löst das, indem es das Verhalten von register_globals selbst nachbaut – unabhängig davon, wie der Server konfiguriert ist. In globals.php läuft eine Funktion durch $_FILES, $_ENV, $_GET, $_POST, $_COOKIE und $_SERVER und kopiert jeden Wert einzeln in $GLOBALS, damit der restliche Code sich überall gleich verhält – egal ob register_globals auf dem jeweiligen Server nun an oder aus war. Der einzige eingebaute Schutz ist eine kurze Liste verbotener Variablennamen wie _files oder _env, damit niemand aus Versehen die Superglobals selbst überschreibt.

Eine Architekturentscheidung, die 2005 für plattformübergreifende Kompatibilität sorgte, ist 2026 ein Sicherheitsrisiko – nicht weil sich der Code geändert hat, sondern weil sich der Konsens darüber, was akzeptabel ist, seitdem verschoben hat.

Eine Blacklist statt einer Allowlist als Sicherheitskonzept – heute würde das kein Code-Review überstehen, ohne dass jemand nachfragt, warum nicht einfach nur die erlaubten Felder durchgelassen werden. Damals war das jedoch keine bewusste Kompromissentscheidung gegen Sicherheit, sondern schlicht der übliche Umgang mit einem Feature, dessen volle Tragweite als Angriffsvektor erst in den Folgejahren allgemein verstanden wurde.

Was diese Rückschau nicht ist: eine Kritik an den Entwicklern von damals. Die Alternative wäre gewesen, hunderte Dateien komplett umzuschreiben, um $_POST[...] konsequent zu verwenden – für ein Open-Source-Projekt mit begrenzten Ressourcen keine realistische Option. Und es ist auch keine Aussage über Joomla heute, das sich seit Version 1.0 komplett neu aufgestellt hat. Es ist eine Momentaufnahme eines Kompromisses, der unter den Bedingungen seiner Zeit Sinn ergab.

Die Idee war der Zeit voraus

Was mich damals an Joomla und Mambo begeistert hat, war nie der Code – es war das Modell. Components, Modules, Templates, Mambots (die späteren Plugins): eine klare Trennung zwischen dem, was eine Seite kann (Components), wie sie zusammengesetzt wird (Templates), was zusätzlich reingehängt wird (Modules) und wie man sich in Kernprozesse einklinkt, ohne den Kern anzufassen (Mambots). Ich weiß noch, wie revolutionär sich das damals anfühlte: eine eigene Component zu bauen war kein Hexenwerk, ein paar Konventionen befolgen, ein paar Dateien anlegen, fertig. Dass man dabei ganz selbstverständlich auch mal direkt im Core herumgehackt hat, wenn eine Komponente nicht ganz das tat, was man wollte, gehörte genauso dazu – das war damals kein Sakrileg, sondern normaler Werkzeugkasten.

Natürlich war Joomla 1.0 keine Hexagonal Architecture im heutigen Sinne. Aber rückblickend erkennt man darin erstaunlich viele Ideen wieder, die heute unter Begriffen wie Hexagonal Architecture oder Ports-and-Adapters diskutiert werden: ein stabiler Kern, klar definierte Erweiterungspunkte, und Fachlichkeit, die von außen angedockt wird statt in den Kern hineinzuwuchern. Nur dass 2005 die Werkzeuge fehlten, um das sauber durchzusetzen. Es gab kein Composer, keine Namespaces (die kamen erst mit PHP 5.3), keine Interfaces im heutigen Sinne, keine verbindlichen Coding-Standards für Erweiterungen. Also musste die Trennung über Konventionen laufen: bestimmte Funktionsnamen, bestimmte Include-Reihenfolgen, bestimmte globale Variablen, die “zufällig” an der richtigen Stelle gesetzt waren.

Das Ergebnis: Ein Architekturmodell, das im Prinzip richtig war, aber technisch nur so stabil sein konnte wie die Disziplin der Entwickler, die sich an die Konventionen hielten. Sobald jemand direkt in eine Datenbanktabelle schrieb statt über die vorgesehene Schnittstelle, war die Trennung durchbrochen – und niemand hätte es beim Kompilieren gemerkt, weil es schlicht nichts zu kompilieren gab, das solche Verstöße hätte verhindern können.

Gute Architektur-Ideen scheitern selten am Konzept. Sie scheitern daran, dass die Sprache und das Ökosystem drumherum noch nicht so weit sind, das Konzept auch durchzusetzen.

Vier Konzepte, die bis heute überlebt haben

Wenn man allen Ballast entfernt und nur den eigentlichen Request-Lifecycle betrachtet, bleibt ein Modell übrig, das verblüffend modern wirkt: Eine Anfrage trifft auf den Front Controller, der lädt die passende Component, die Component liefert Inhalt, der ins Template gerendert wird, ergänzt durch Module drumherum, und an mehreren Stellen dazwischen können sich Mambots einklinken.

Components waren die eigentlichen Anwendungen – ein Kontaktformular, ein Forum, eine Artikelverwaltung. Jede Component war für sich lauffähig und austauschbar, ohne dass der Rest der Seite davon wusste. Rückblickend erinnert das an die Idee, Fachlichkeit in klar abgegrenzten Bereichen zu organisieren – wenn auch deutlich einfacher, als wir es heute unter Domain-Driven Design verstehen.

Modules waren die kleinen, unabhängigen Bausteine drumherum – ein Login-Kasten, eine Liste der neuesten Artikel, ein Werbebanner. Sie kannten die Component nicht, auf deren Seite sie angezeigt wurden, und die Component kannte sie nicht. Diese Entkopplung war damals mehr Konvention als erzwungene Regel, aber die Idee dahinter war schon die eines lose gekoppelten Widget-Systems.

Templates trennten Layout von Inhalt. Ein Template wusste nichts über die Fachlichkeit der Component, es wusste nur, wo Module und Component-Output hingehören. Das ist derselbe Gedanke, der heute in jeder Trennung von View und Domain-Logik steckt.

Mambots (die späteren Plugins) waren die Erweiterungspunkte im Kern selbst. Die mit Abstand gebräuchlichste Form waren Content-Mambots: Man schrieb ein Tag wie {mambot_name} in einen Artikel, und beim Rendern ersetzte der passende Mambot dieses Tag durch seine eigene Ausgabe – etwa um ein Bild einzufügen oder Flash einzubetten. Das System selbst war aber von Anfang an breiter angelegt als nur dieser eine Fall: Es gab auch Such-Mambots, die Component-Inhalte durchsuchbar machten, oder Editor-Mambots, die den WYSIWYG-Editor austauschten. Bemerkenswert ist, dass Joomla damit bereits mehrere unterschiedliche Mambot-Typen kannte – Content war nur einer davon. Die Grundidee eines ereignisbasierten Erweiterungssystems war also vorhanden, lange bevor Begriffe wie Event Dispatcher oder Middleware im PHP-Umfeld verbreitet waren.

Keines dieser vier Konzepte ist heute überholt. Nur die Mittel, mit denen man sie umsetzt, haben sich seitdem komplett verändert.

Was übrig bleibt, wenn man den Ballast wegnimmt

Der Gedanke, Joomla 1.0 auf PHP 8.3 zu portieren, klingt reizvoll, solange man nur an den Kern denkt. Der ist tatsächlich schlank: Der eigentliche Request-Lifecycle besteht aus überraschend wenig Code – Front Controller, Routing, Laden der Component, Rendern des Templates. Der Rest entsteht durch Erweiterungen. Das Problem ist der Rest.

Es gab 2005 kein Composer, keinen Paketmanager, kein Packagist. Bibliotheken waren keine Abhängigkeiten, sondern Teil der Anwendung: Man kopierte ihren Quellcode in den includes-Ordner und hoffte, ihn nie wieder anfassen zu müssen – fest verdrahtet, oft ohne Versionsnummer, manchmal mit eigenen Anpassungen direkt im Bibliothekscode. Jede dieser mitgeschleppten Bibliotheken müsste einzeln bewertet, ersetzt oder modernisiert werden, bevor an PHP 8.3 überhaupt zu denken wäre.

Dazu kommt, dass die Trennung von Zuständigkeiten, die auf Architekturebene existierte, auf Code-Ebene oft nicht eingehalten wurde. SQL-Statements finden sich an vielen Stellen des Codes, häufig eng mit Präsentationslogik vermischt, und auch das Installationsscript mischt HTML und PHP recht unbekümmert – das sind keine Ausrutscher einzelner Dateien, das zieht sich durch die Basis. Eine Portierung wäre am Ende keine Portierung, sondern eine Neuentwicklung, die zufällig dasselbe Architekturmodell verwendet. Der “schlanke Kern” ist nur schlank, solange man nicht vorhat, ihn tatsächlich zum Laufen zu bringen.

Der Gedanke lebt weiter, nur besser gebaut

Was bleibt, ist die Erkenntnis, dass die Grundidee von damals – ein stabiler Kern, Fachlichkeit über klar definierte Erweiterungspunkte – nichts von ihrer Berechtigung verloren hat. Nur die Mittel, sie umzusetzen, haben sich radikal verändert. Interfaces statt Konventionen. Dependency Injection statt globaler Variablen. Ein Paketmanager statt eines Ordners voller kopierter Bibliotheken. Namespaces statt Include-Guards.

Genau dieses Prinzip – ein stabiler Kern, an den sich Fachlichkeit über saubere Schnittstellen andockt, ohne ihn zu verändern – ist heute mit den richtigen Werkzeugen tatsächlich durchsetzbar, nicht nur als Konvention gedacht. Der Unterschied zwischen 2005 und heute ist nicht die Idee. Es ist, dass man sie heute nicht mehr auf Vertrauen bauen muss.

Der Sonntagnachmittag, der nie kam

Die Portierung bleibt eine Schublade-Idee. Vielleicht wird daraus irgendwann ein reines Gedankenexperiment an einem verregneten Sonntagnachmittag – ohne den Anspruch, dass am Ende tatsächlich etwas läuft.

Vielleicht fasziniert mich Joomla 1.0 heute aus demselben Grund wie alte Betriebssysteme oder frühe Frameworks: Man erkennt darin Ideen, die bis heute überlebt haben – nur in deutlich besseren Werkzeugen verpackt. Zwischen all den globalen Variablen und Includes steckt überraschend viel Architektur. Nicht der Code ist alt geworden. Seine Annahmen über die Welt sind es.