Vorschau auf WoltLab Community Framework 2.0

      Vorschau auf WoltLab Community Framework 2.0

      Ich möchte euch hier nach und nach einen Ausblick auf die nächste Version von WoltLab Community Framework (kurz: WCF) geben. Entgegen ursprünglicher Planungen haben wir uns dazu entschlossen, dass die nächste Version des Frameworks die Versionsnummer 2.0 und nicht 1.2 tragen wird. Zu begründen ist dies mit den zum Teil sehr umfangreichen Änderungen, die sich im Kern des Frameworks bereits in der laufenden Entwicklung ergeben haben.

      Hauptziel für Version 2.0 ist es, eine durchweg einheitliche API zur Verfügung zu stellen und deutlich mehr häufig benötigte Funktionen als abstrakte, modulare Funktionalität zur Verfügung zu stellen. Als Beispiel wäre hier die Melden-Funktion zu nennen, die bisher nur im Forum existiert. WCF 2.0 wird hier eine abstrakte Melden-Funktion zur Verfügung stellen, die dann an verschiedenen Stellen angesprochen werden kann (Forum, Galerie, Blog usw.). Alle Meldungen laufen dann an einer zentralen Stelle zusammen.

      Als wesentliche Änderungen sind derzeit geplant:
      • Einheitliche API
      • Unterstützung mehrerer Datenbanken (derzeit konkret: MySQL, PostgreSQL)
      • Zentrale/abstrakte Lösungen für Melden, Benachrichtigungen, Moderation (Freischaltung), Gelesen-Markierung, Bewerten/Likes, AJAX-Funktionen
      • Einfache Verbindung von mehreren Endanwendungen (u. a. Multi-Domain-Support)
      • Cleanup-System
      • Template-Listener
      • Nutzung von HTML 5
      • Nutzung von jQuery statt Prototype

      Auf die einzelnen Punkte werde ich im weiteren Verlauf des Themas detailliert eingehen.
      Marcel Werk
      WoltLab CEO
      WoltLab Community Framework 2.0 basiert auf PHP 5.3. Der Sprung von PHP 5.0 auf 5.3 bietet sehr viele neue Möglichkeiten bei der Weiterentwicklung unserer Software. Auf die für uns wesentlichen Änderungen, und welche Vorteile sie uns bringen, möchte ich hier kurz eingehen. Achtung: Nicht-Entwickler können diesen Eintrag getrost überspringen.


      Namespaces
      "Namespaces" helfen uns die Klassen des Frameworks besser zu strukturieren und Benenunngskonflikte zu vermeiden. Darüber hinaus fällt das manuelle Einbinden von Klassendateien weg, da dies nun komplett automatisch über den Autoloader erfolgt. Konkret sehen die Änderungen hier wie folgt aus (Beispiel der Klasse "AbstractPage"):

      WCF 1.1:

      PHP Source Code

      1. <?php
      2. require_once(WCF_DIR.'lib/page/Page.class.php');
      3. require_once(WCF_DIR.'lib/system/event/EventHandler.class.php');
      4. abstract class AbstractPage implements Page {

      WCF 2.0:

      PHP Source Code

      1. <?php
      2. namespace wcf\page;
      3. use wcf\system\WCF;
      4. use wcf\system\event\EventHandler;
      5. abstract class AbstractPage implements Page {


      Late Static Bindings
      "Late Static Binding" bietet die Möglichkeit, eine aufgerufene Klasse im Kontext statischer Vererbung zu referenzieren. WCF 2.0 stellt im Bereich der DatabaseObjects auch dank "Late Static Bindings" eine sehr umfangreiche Basisimplementation zur Verfügung, die das Implementieren einer API für eigene Objekte sehr einfach macht, da nur sehr wenig Quellcode geschrieben werden muss. Als konkretes Beispiel wäre hier die "create" Methode in der DatabaseObjectEditor-Klasse zu nennen:

      PHP Source Code

      1. <?php
      2. namespace wcf\data;
      3. use wcf\system\exception\SystemException;
      4. use wcf\system\WCF;
      5. abstract class DatabaseObjectEditor extends DatabaseObject implements EditableObject {
      6. /**
      7. * name of the base class
      8. * @var string
      9. */
      10. protected static $baseClass = '';
      11. /**
      12. * @see EditableObject::create()
      13. */
      14. public static function create(array $parameters = array()) {
      15. $keys = $values = '';
      16. $statementParameters = array();
      17. foreach ($parameters as $key => $value) {
      18. if (!empty($keys)) {
      19. $keys .= ',';
      20. $values .= ',';
      21. }
      22. $keys .= $key;
      23. $values .= '?';
      24. $statementParameters[] = $value;
      25. }
      26. // save object
      27. $sql = "INSERT INTO ".static::getDatabaseTableName()."
      28. (".$keys.")
      29. VALUES (".$values.")";
      30. $statement = WCF::getDB()->prepareStatement($sql);
      31. $statement->execute($statementParameters);
      32. // return new object
      33. $id = WCF::getDB()->getInsertID(static::getDatabaseTableName(), static::getDatabaseIndexName());
      34. return new static::$baseClass($id);
      35. }
      Marcel Werk
      WoltLab CEO
      Auch im SQL-Datenbank-Bereich wird WoltLab Community Framework 2.0 einen großen Sprung nach vorne machen. Der Inhalt dieses Beitrags richtet sich wieder an Entwickler. ;)


      PHP Data Objects ("PDO")

      Zukünftig erfolgt die gesamte Datenbankunterstützung direkt über die sogenannten PDOs ("PHP Data Objects"). Diese ermöglichen einen konsistenten Zugriff auf die spezifischen Datenbanksystemen, ohne auf spezifische Eigenheiten, wie zum Beispiel die Verbindungsverwaltung oder bei der Verwendung von "Prepared Statements" (siehe nächster Absatz), näher eingehen zu müssen.

      WCF 2.0 stellt über die bekannten Abstraktionswege eine einheitliche Schnittstelle für den Zugriff bereit. Unter Verwendung von Standard-konformen SQL-Abfragen muss ein Entwickler keine Kenntnis der verwendeten Datenbank haben, dies ist insbesondere in Hinblick auf PostgreSQL relevant. Zugleich legt die Verwendung von PDO den Grundstein für die mögliche Unterstützung weiterer Datenbanksystemen in der Zukunft.


      Prepared Statements

      SQL-Injections sind der Inbegriff von Sicherheitslücken durch nicht-abgesicherte Parameter und stellen ein enormes Risko für die Stabilität und Integrität einer Software dar - ein vergessener Parameter kann bereits fatale Folgen mit sich ziehen.

      Einer der Vorteile von "Prepared Statements" ist die Bekanntheit der anzusprechenden Felder (und somit implizit der möglichen Werte) vor der Übermittlung der tatsächlichen Daten an die Datenbank. Im ersten Schritt wird die gewünschte Abfrage auf Basis von abstrakten Parameterwerten, repräsentiert durch das Fragezeichen in der unten stehenden Abfrage, an die Datenbank übermittelt und für die weitere Verwendung vorgehalten.

      Der nachfolgende Schritt beinhaltet die Ausführung der zuvor vorbereiteten Abfrage unter Verwendung der Methode execute(). Die zu übermittelten Werte werden explizit nicht durch gängige Methoden wie z. B. escapeString() abgesichert, dies erfolgt vollkommen automatisiert bei der Übermittlung der Daten.

      Konkret sieht das dann z. B. so aus:

      PHP Source Code

      1. $sql = "DELETE FROM wcf".WCF_N."_user WHERE userID = ?";
      2. $statement = WCF::getDB()->prepareStatement($sql);
      3. foreach ($userIDs as $userID) {
      4. $statement->execute(array($userID));
      5. }


      Das obige Beispiel vermittelt anschaulich einen weiteren Vorteil bei der Nutzung von Prepared Statements: Die SQL-Abfrage wird nur ein einziges mal an das Datenbanksystem übermittelt und dort zuerst lokal analysiert. Eine von der Datenbank vorgehaltene Abfrage kann so beliebig oft mit den gewünschten Parametern befüllt werden, ohne bei jedem Aufruf die gesamte Abfrage erneut übermitteln zu müssen. Die Effektivität wird gerade bei umfangreichen Datenmanipulationen ersichtlich, wenn beispielsweise viele Themen gleichzeitig gelöscht werden sollen oder im Rahmen der Paketinstallation.

      Prepared Statements leisten einen wichtigen Beitrag zur weiteren Verbesserung der Geschwindigkeit, Stabilität und Sicherheit von WoltLab Community Framework.


      PostgreSQL

      WCF 2.0 wird, neben der bereits bekannten Unterstützung von MySQL, ein weiteres, populäre Datenbanksystem unterstützen: PostgreSQL. Bei PostgreSQL handelt es sich um eine sehr fortschrittliche, kostenlose und schnelle Alternative zur weit verbreiteten Datenbank MySQL. PostgreSQL ist - im Gegensatz zu MySQL - vollständig Open Source und ist somit völlig unabhängig von wirtschaftlichen Interessen, die zuletzt bei MySQL die Abspaltung in die "MariaDB" bewirkt haben.

      Ein weiterer Vorteil von PostgreSQL ist die konsequente Umsetzung des SQL-Standards mit relativ geringen Abweichungen, im Vergleich zu anderen Datenbanksystemen. Zudem ist PostgreSQL vor allem mit dem Fokus auf große und umfangreiche Datenbanken ausgelegt, stellt ein sehr hohes Maß an Stabilität sicher und setzt strikt auf die Integrität der enthaltenden Daten.


      Foreign Keys (zu Deutsch: "Fremdschlüssel")

      Foreign Keys erlauben die Verknüpfung zusammengehöriger Datensätze als logische Verbindungen über mehrere Datenbanktabellen hinweg. Ein Hauptaugenmerk bei der Entwicklung von WCF 2.0 ist die Konsistenz der Datenbank und all ihrer enthaltenen Daten, sowie die Vermeidung von so genannten "Datenleichen" (Daten die keinen Bezug mehr haben und nicht länger existieren sollten). Eine Vielzahl von Aktionen die auf eine Massenverarbeitung ausgelegt sind, können nicht immer wirklich alle referenzierten Daten löschen, etwa wenn das Script zu einem Zeitpunkt X einfach abbricht oder ein anderer Fehler auftritt.

      Um zu verhindern dass die Datenbank verwaiste Datensätze enthält, die mitunter Fehler verursachen können, setzen wir konsequent auf den Einsatz von Foreign Keys. Bei der Löschung eines Benutzers werden beispielsweise automatisch alle unmittelbar mit ihm verknüpften Daten gelöscht oder mindestens einem Pseudo-Benutzer (bei Beiträgen am Rang "unregistriert" erkennbar) zugewiesen. Dies bedeutet, dass Forenbeiträge grundsätzlich erst einmal erhalten bleiben, persönliche Einstellungen oder Notizen jedoch sofort entfernt werden.
      Alexander Ebert
      Senior Developer WoltLab® GmbH


      Wir haben uns dazu entschlossen, die weitere Entwicklung von WoltLab Community Framework 2.0 und den dazugehörigen LGPL-Paketen auf GitHub auszulagern. Das Projekt ist dort öffentlich zugänglich und bietet somit volle Transparenz für Entwickler, die sich über den Entwicklungsfortschritt informieren wollen. Darüber hinaus ist es auf Github sehr einfach, Forks zu erstellen und sich über Pull Requests direkt an der Weiterentwicklung des Frameworks zu beteiligen. Entwickler sind herzlich dazu eingeladen, sich entsprechend zu beteiligen.

      WoltLab Community Framework 2.0 auf GitHub
      Marcel Werk
      WoltLab CEO
      Template-Modifikationen durch Plugins

      Praktisch jedes Plugin modifiziert aktuell die Template-Ausgabe, indem es vorgegebene Platzhalter (Template-Variablen) nutzt und mit Inhalt befüllt, oder ganze Templates mit Hilfe von Template-Patches umschreibt. Beide Methoden bieten die Möglichkeit, nahezu beliebige Änderungen durchzuführen, haben leider aber auch Nachteile bzw. Schwächen. Wir haben uns dazu entschieden mit WCF 2.0 die bekannten Template-Platzhalter, sowie die Template-Patches zu streichen und stattdessen ein neues System einzuführen - die so genannten Template-Events.


      Zunächst kurz zu den Schwächen der alten Möglichkeiten:

      Template-Platzhalter

      Die Platzhalter bieten eine einfache Möglichkeit, zusätzliche Inhalte an vielen Stellen einfach hinzufügen, aber sie haben zwei entscheidene Nachteile. Zum einen benötigt man zum Befüllen der Platzhalter jedes Mal einen Event-Listener, selbst für kleinere Änderungen. Zum anderen müssen die Platzhalter vor dem Ausführen des eigentlichen Templates befüllt werden, weshalb man nicht auf die im Template zur Verfügung stehenden Variablen zugreifen und entsprechend reagieren kann.


      Template-Patches

      Template-Patches bieten grundsätzlich die größtmögliche Freiheit bei der Modifikation von Templates - praktisch jede beliebige Modifikation ist mit ihnen umsetzbar. Leider sind Template-Patches aber auch sehr fehleranfällig. Sie basieren auf dem Diff-Algorithmus, der wiederum leider sehr anfällig für Änderungen am Basis-Template ist. Sobald an einer bestimmten Stelle ein Plugin einen Template-Patch angewendet hat, oder sich das Basis-Template durch ein Update oder einen individuellen Stil verändert hat, kann ein Patch nicht mehr angewendet werden und die Installation eines Plugins schlägt fehl.


      Template-Events *NEU*

      Template-Events sind Schnittstellen im Template, in die man sich einklinken kann. Die Funktionsweise ist praktisch identisch zu den bekannten Event-Listener, mit denen sich der Programmablauf PHP-seitig bereits individuell manipulieren lässt.

      Ein Beispiel, wie das in der Praxis aussehen könnte:

      Source Code

      1. <nav class="headerNavigation">
      2. <div>
      3. <ul>
      4. {event name='headerNavigation'}
      5. <li id="toBottomLink" class="toBottomLink"><a href="#bottom" title="{lang}wcf.global.scrollDown{/lang}" class="balloonTooltip"><img src="{@RELATIVE_WCF_DIR}icon/toBottom.svg" alt="" /> <span class="invisible">{lang}wcf.global.scrollDown{/lang}</span></a></li>
      6. </ul>
      7. </div>
      8. </nav>

      {event name='headerNavigation'} bietet hier die Möglichkeit, sich unter dem Event-Namen "headerNavigation" in dieses Template einzuklinken. Die Registrierung für dieses Template-Event erfolgt, wie bei den Event-Listenern, mittels XML-Dokument:

      Source Code

      1. <import>
      2. <templatelistener name="myHeaderNavigation">
      3. <environment>user</environment>
      4. <templatename>header</templatename>
      5. <eventname>headerNavigation</eventname>
      6. <templatecode><![CDATA[<li><a href="http://www.woltlab.com">www.woltlab.com</a></li>]]></templatecode>
      7. </templatelistener>
      8. </import>


      Template-Events bieten folgende Vorteile:
      • Der Templatecode der Template-Events wird erst zur Laufzeit des Templates ausgeführt. Ein Zugriff auf die Variablen des Templates ist also möglich. Dabei ist diese Funktion auch noch sehr performant, da der Templatecode eines Events statisch in das kompilierte Template eingebunden wird.
      • Zusätzliche Event-Listener werden nur benötigt, wenn Daten angezeigt werden sollen, die im Template nicht bereits zur Verfügung stehen. Für kleinere Änderungen werden also oftmals keine zusätzliche Event-Listener benötigt.
      • Template-Events haben keine Probleme mit Updates und funktionieren auch in individuell veränderten Templates, etwa von Stilen, ohne Probleme.
      Marcel Werk
      WoltLab CEO