Migration von 2.1 auf 3.0

1. Einleitung

Version 3.0 des Adventure PHP Framework beinhaltet einen von Grund auf neu geschriebenen Parser. Auf diesem basieren eine Vielzahl von neuen Funktionen und Optimierungen.

Mit dem Erscheinen dieses Release wird die Weiterentwicklung der Version 2.1 eingestellt. Security- und Bug-Fixes werden bis Ende 2015 verfügbar sein.

In diesem Artikel erfahren Sie, wie Sie eine bestehende Anwendung auf APF-Version 3.0 aktualisieren können. Wie bereits von den vorangegangenen Versionen gewohnt, kann die Migration auch in diesem Release weitestgehend automatisiert erfolgen. Zu diesem Zweck liegen zwei Migrations-Skripten bei: migrate-code.sh für die Aktualisierung von HTML- und PHP-Dateien und migrate-config.sh für Konfigurations-Dateien.

Das APF-Team empfiehlt, die Nutzung der Migrations-Skripten einer manuellen Migration vorzuziehen. Dies spart Zeit und Kosten und stellt sicher, dass alle relevanten Stellen erfasst wurden.

Die folgenden Kapitel stellen die notwendigen Schritte zum Umstieg auf Version 3.0 zusammen.

2. Automatisierte Migration

Wie von Release 2.1 bereits bekannt, lässt sich die Migration mit Hilfe eines mitgelieferten Migrations-Skripts erledigen. In diesem Release wurden Änderungen an Code- und Konfigurations-Dateien vorgenommen, die mit Hilfe der Skripte migrate-code.sh und migrate-config.sh auf Ihre bestehende Applikation übertragen werden können.

Bitte stellen Sie sicher, dass Sie vor der Migration alle unter Migration von 2.0 auf 2.1 beschriebenen Schritte durchgeführt haben. Setzen Sie eine noch ältere Version ein, migrieren Sie bitte zuerst vollständig auf die Version 2.1. Hinweise zur jeweiligen Migration finden Sie auf der Seite Artikel.
Bitte erstellen Sie vor der Ausführung der Skripten eine Sicherungskopie Ihrer Anwendung um bei Bedarf Inhalte wieder herstellen zu können!

Sofern Sie auf Windows entwickeln, installieren Sie bitte eine aktuelle Version von cygwin (Download unter cygwin.com). Eine Migration auf Basis eines Windows-Batch-Script wird nicht angeboten.

Um Ihre Quell-Code-Dateien auf den neuesten Stand zu bringen, führen Sie das Skript bitte wie folgt aus:

Bitte wechseln Sie vor der Ausführung in das Code-Verzeichnis Ihrer Applikation um unerwartete Ergebnisse zu vermeiden.
Code
$ /path/to/APF/migration/migrate-code.sh /cygdrive/c/xampp/php/php ############################################# # APF 3.0 automatic code migration # ############################################# Checking PHP executable available ... [OK] Using given php executable at /cygdrive/c/xampp/php/php. PHP Version: 5.4.16. ############################################# Starting migration ... * Migrate taglib declaration statements ... * Consolidate tag usage ... * Remove redundant tag lib registration ... * Switch to new place holder logic ... ############################################# Migration done! Please check your code and follow instructions within migration documentation!

Um Ihre Konfigurations-Dateien auf den neuesten Stand zu bringen, führen Sie das Skript bitte wie folgt aus:

Bitte wechseln Sie vor der Ausführung in das Konfigurations-Verzeichnis Ihrer Applikation um unerwartete Ergebnisse zu vermeiden.
Code
$ /path/to/APF/migration/migrate-config.sh /cygdrive/c/xampp/php/php ############################################# # APF 3.0 automatic configuration migration # ############################################# Checking PHP executable available ... [OK] Using given php executable at /cygdrive/c/xampp/php/php. PHP Version: 5.4.16. ############################################# Starting configuration migration ... * Migrate cache configuration ... * Migrate pager configuration ... ############################################# Migration done! Please check your configuration and follow instructions within migration documentation!

3. Manuelle Schritte

Das Migrations-Skript kann leider nicht alle Projekt-spezifischen Konstrukte migrieren. Die folgenden Kapitel zeigen, welche manuellen Schritte notwendig sind um bestehenden Code mit der Version 3.0 einsetzen zu können:

3.1. Front-Controller

Mit dem Issue ID#207 wurde eine lange in der Code-Basis vorhandene Klassen-Design-Schwäche entfernt. Die Klasse APFObject ist nun nicht mehr für die Speicherung von XML-Tag-Attributen zuständig - dies wurde in die Klasse Document verschoben, denn diese repräsentiert einen APF-DOM-Knoten.

Mit dieser Änderung geht auch eine Änderung der Klasse FrontControllerInput einher, die Action-Parameter nun in einem eigenen Feld speichert: $this->parameters.

Um auf die neue Version zu aktualisieren, ersetzen Sie bitte alle Vorkommen in Klassen, die von FrontControllerInput ableiten wie folgt:

Bisheriger Name Neuer Name
(APFObject) $this->attributes (FrontControllerInput) $this->parameters
FrontControllerInput::getAttribute() FrontControllerInput::getParameter()
FrontControllerInput::getAttributes() FrontControllerInput::getParameters()
FrontControllerInput::setAttribute() FrontControllerInput::setParameter()
FrontControllerInput::setAttributes() FrontControllerInput::setParameters()
FrontControllerInput::deleteAttribute() (mehr unterstützt)
FrontControllerInput::addAttribute() (mehr unterstützt)

3.2. APFObject

Wie in Kapitel 3.1 angesprochen wurde das Attribut-Handling wurde aus APFObject entfernt. Diese Änderung betrifft damit auch Klassen, die direkt von APFObject erben und z.B. bei der Implementierung von View-Models auf das $this->attributes-Feld zurückgreifen.

Passen Sie beim Umstieg auf die neue Version bitte die relevanten Stellen an.

3.3. Platzhalter

Die Funktion des <html:placeholder />-Tags im Inhaltsbereich eine eigene, vereinfachte Platzhalternotation nutzen zu können wurde in Release 3.0 entfernt.

Finden Sie in Ihrem Projekt Code-Stellen wie

APF-Template
<html:placeholder name="foo">This is a {bar} thing.</html:placeholder>

schreiben Sie diese bitte wie folgt um:

APF-Template
This is a ${bar} thing.

Im Controller ändert sich die Verwendung des Platzhalters von bisher

PHP-Code
$this->setStringPlaceHolder('foo', 'bar', '...');

auf

PHP-Code
$this->setPlaceHolder('bar', '...');

3.4. Signatur-Änderung DateSelectorTag und TimeSelectorTag

Mit der Umsetzung von ID#208 wurde die Signatur der Methoden DateSelectorTag::getDate() und DateSelectorTag::setDate() sowie TimeSelectorTag::getTime() und TimeSelectorTag::setTime() geändert.

Die beiden Setter nehmen nun neben einem String auch eine Instanz der Klasse DateTime zur Initialisierung entgegen. Damit ist eine stärkere Typisierung der Daten in Ihrer Anwendung möglich.

Das APF-Team empfiehlt beim Umstieg auf die Version 3.0 Datums- und Zeit-Werte in DateTime-Instanzen zu verpacken. Dies erleichtert nicht nur die Nutzung der beiden Tags, sondern sorgt für eine saubere Kapselung der Werte.

Die Getter-Methoden geben ab der Version ausschließlich DateTime-Instanzen zurück. Dies erfordert im Gegensatz zu den Setter-Methoden eine Änderung Ihrer bestehenden Code-Basis. Sofern Sie bisher für das Auslesen und Verarbeiten eines DateSelectorTag den Code

PHP-Code
$date = &$form->getFormElementByName('date-of-birth'); $sql = 'INSERT INTO ... SET ..., dob = `' . $date->getDate() . '`, ... ';

genutzt haben, muss dieser ab Version 3.0 wie folgt lauten:

PHP-Code
$date = &$form->getFormElementByName('date-of-birth'); $sql = 'INSERT INTO ... SET ..., dob = `' . $date->getDate()->format('Y-m-d') . '`, ... ';

3.5. Neue Request-Implementierung

Im Rahmen der Umsetzung von ID#234 wurden die Klassen RequestHandler und HeaderManager durch eine vollständig objektorientierte Request- und Response-Umsetzung für die Abstraktion der HTTP-Kommunikation ersetzt.

Diese erleichtern die Implementierung und bieten einfache Methoden für gängige Aufgaben wie das Auslesen von Request-Inhalten, die Manipulation von HTTP-Antwort-Headern und die Weiterleitung auf andere URLs.

Die Umstellung auf das neue Modell erfolgt weitestgehend durch die Ausführung der automatisierten Migration, einige Teile müssen jedoch manuell erledigt werden.

Die folgenden Kapitel beschreiben die manuell vorzunehmenden Änderungen um auf die neue Request- und Response-Implementierung umzustellen.

Hinweise zur Request-Verarbeitung im APF 3.0 finden Sie im Kapitel Request-Verarbeitung.

3.5.1. RequestHandler

Die Klasse RequestHandler diente im APF bis Version 2.1 zur Abstraktion des Zugriffs auf die Inhalte der aktuellen Anfrage. Die darin enthaltenen Funktionalitäten sind nun vollständig in der Klasse RequestImpl enthalten.

Bei der Migration werden die einfachen und auch häufigsten Fälle erfasst. Die Nutzung von getValues() kann auf Grund der Komplexität der Anwendung nicht automatisch umgestellt werden. Schreiben Sie daher alle Vorkommen der Methode von z.B.

PHP-Code
$values = RequestHandler::getValues(array('page', 'sort' => 'asc'));

auf

PHP-Code
$page = self::getRequest()->getParameter('page'); $sort = self::getRequest()->getParameter('sort', 'asc');

um. Alternativ lässt sich folgende Implementierung nutzen, die den Eingriff in den Code Ihrer bestehenden Anwendung gering hält:

PHP-Code
$values = array(); $request = self::getRequest(); foreach (array('page', 'sort' => 'asc') as $name => $default) { if (is_integer($name)) { $values[$default] = $request->getParameter($default); } else { $values[$name] = $request->getParameter($name, $default); } }

3.5.2. HeaderManager

Mit Hilfe der Klasse HeaderManager ließen sich im APF bis Version 2.1 die HTTP-Header der Antwort manipulieren und Benutzer zu generierten URLs weiter leiten. Die darin enthaltenen Funktionalitäten sind nun vollständig in der Klasse ResponseImpl aufgegangen.

Die Migration des HeaderManager auf die neuen Funktionen lässt sich größtenteils automatisiert erledigen. Eine Ausnahme bilden Front-Controller-Actions, die in der Regel umfassende Manipulationen der Antwort vornehmen. Aus diesem Grund bitten wir Sie, Actions nach der Migration nochmals genauerer unter die Lupe zu nehmen.

Code-Blöcke wie z.B.

PHP-Code
HeaderManager::send('Content-Type: application/json'); echo json_encode(array( 'headline' => $news->getHeadline(), 'subheadline' => $news->getSubHeadline(), 'content' => $news->getContent(), 'newscount' => $news->getNewsCount() )); exit(0);

werden von der automatisieren Migration zu

PHP-Code
self::getResponse()->setHeader(new HeaderImpl('Content-Type', 'application/json')); echo json_encode(array( 'headline' => $news->getHeadline(), 'subheadline' => $news->getSubHeadline(), 'content' => $news->getContent(), 'newscount' => $news->getNewsCount() )); exit(0);

umgeschrieben. Dies führt dazu, dass der gesendete Content-Type-Header nicht beim Browser ankommt, da die ResponseImpl-Instanz nicht mit zurück gesendet wird. Ändern Sie den Code daher manuell wie folgt ab:

PHP-Code
$response = self::getResponse(); $response->setHeader(new HeaderImpl('Content-Type', 'application/json')); $response->setBody(json_encode(array( 'headline' => $news->getHeadline(), 'subheadline' => $news->getSubHeadline(), 'content' => $news->getContent(), 'newscount' => $news->getNewsCount() ))); $response->send();

3.6. Interface-Änderung Konfiguration

Im Rahmen der Umsetzung von Issue ID#224 wurde das Interface Configuration dahingehend geändert, dass die Methode getSection() unter allen Umständen eine Instanz einer Configuration-Implementierung zurück liefert. Dies hilft auf sehr effektive Weise Null Pointer Exceptions zu vermeiden und damit den Code für den Betrieb sicherer zu gestalten.

Mit der Änderung wurde die Methode hasSection() eingeführt, die erlaubt das Vorhandensein einer Sektion abzufragen.

Um Ihre Applikation mit Version 3.0 fachlich korrekt betreiben zu können ist eine Änderung der Implementierung - speziell bei den Prüfungen auf Vorhandensein einer Sektion - notwendig. Haben Sie bisher Code-Schnipsel wie

PHP-Code
if ($config->getSection($this->getLanguage()) !== null) { ... } else { ... }

in Ihrer Applikation genutzt, so sind siese wie folgt umzuschreiben:

PHP-Code
if ($config->hasSection($this->getLanguage())) { ... } else { ... }

3.7. ServiceManager API

APF 3.0 führt das Konzept der constructor injection zur Erzeugung von Objekten und Services ein. Dies erleichtert die Erzeugung und gleichzeitige Initialisierung von Objekten und ermöglicht, Abhängigkeiten direkt im Konstruktor auszurücken.

Die automatisierte Migration erfasst bereits alle Aufrufe von APFObject::getServiceObject() die Aufrufe von ServiceManager::getServiceObject() werden allerdings nicht abgedeckt. Bitte prüfen Sie Ihre Applikation, ob die Methode genutzt wird und aktualisieren Sie beispielsweise die Code-Stelle

PHP-Code
$model = & ServiceManager::getServiceObject( 'CMS\core\biz\CMSModel', $context, 'de', APFService::SERVICE_TYPE_SESSION_SINGLETON );

zu

PHP-Code
$model = & ServiceManager::getServiceObject( 'CMS\core\biz\CMSModel', $context, 'de', [], APFService::SERVICE_TYPE_SESSION_SINGLETON );

ab.

Bitte beachten Sie zudem, dass die als veraltet gekennzeichneten Methoden APFObject::getAndInitServiceObject() und ServiceManager::getAndInitServiceObject() entfernt wurden. Nutzen Sie statt dessen bitte die unter Erzeugung von Objekten und Services beschriebenen Möglichkeiten.

4. FAQ

Die folgenden Kapitel beinhalten häufig auftretende Fehler und mögliche Lösungen:

4.1. Fehlende Tag-Registrierung

Frage

Beim Aufruf einer Seite tritt folgender Fehler auf:

Code
Fatal error: Uncaught exception 'APF\core\pagecontroller\ParserException' with message ' in ***/APF/core/pagecontroller/Document.php on line 1037 APF\core\pagecontroller\ParserException: No tag definition found for prefix "template" and name "placeholder" in document with type "APF\core\pagecontroller\TemplateTag"! Template code: [..]
Antwort

Migrations-Skript wurde noch nicht ausgeführt oder der Tag wurde im aktuellen Projekt (noch) nicht registriert. Bitte füge Sie ein <core:addtaglib /> zu Ihrem Template hinzu oder registrieren den Tag in der index.php via Document::addTagLib(...);.

4.2. Altes Platzhalter-Format

Frage

Beim Aufruf der Seite tritt folgender Fehler auf:

Code
No closing tag found for tag <html:placeholder />! Template code: <html:placeholder name="anchor"> <a href="{URL}" title="{TITLE}" class="breadcrumb {CLASSES}" >{TEXT}</a></template:placeholder>
Antwort

Die Verwendung von String Placeholdern ist in Version 3.0 nicht mehr vorhanden. Lösen Sie diese bitte wie in Kapitel 3.3 beschrieben auf. Für die vorliegende Fehlermeldung ist folgende Lösung möglich:

APF-Template
<a href="${URL}" title="${TITLE}" class="breadcrumb ${CLASSES}">${TEXT}</a>

Bitte beachten Sie, dass der zugehörige Controller ebenfalls angepasst werden muss.

5. Weitere Optimierungen

Tipps für den Umstieg auf Version 3.0 und Vorschläge zur Optimierung Ihrer Anwendung finden Sie im Artikel Umstieg von 2.1 auf 3.0.

Kommentare

Möchten Sie den Artikel eine Anmerkung hinzufügen, oder haben Sie ergänzende Hinweise? Dann können Sie diese hier einfügen. Die bereits verfassten Anmerkungen und Kommentare finden Sie in der untenstehenden Liste.
Für diesen Artikel liegen aktuell keine Kommentare vor.