• ZF1 Projekt vorbereiten
• ZF2 Skeleton integrieren
• Startseite nach ZF2 migrieren
• MVC-Layer für Blog Modul migrieren
• Authentifizierung & Autorisierung migrieren
• Model-Layer für Blog Modul migrieren
Ralf EggertGeschäftsführer / Gründer um Travello GmbH
3. Und Sie?
• Erfahrungen ZF1?
• Erfahrungen ZF2?
• Projekte schon migriert?
• Falls nein, warum nicht?
• Falls ja, Ihre Erfahrungen?
4. Überblick
• Einführung
• ZF1 Projekt vorbereiten
• ZF2 Skeleton integrieren
• Startseite nach ZF2 migrieren
• MVC-Layer für Blog Modul migrieren
• Authentifizierung & Autorisierung migrieren
• Model-Layer für Blog Modul migrieren
6. Ausreden Gründe gegen Migration
• Technische Gründe
– PHP 5.3
• Betriebswirtschaftliche Gründe
– Kunde scheut den Aufwand
• Fachliche Gründe
– Know-how fehlt
• Organisatorische Gründe
– Keine Zeit
7. Gründe für Migration
• Zend Framework 1
– Keine Weiterentwicklung
– Support durch Zend wird 2014 eingestellt
• Zend Framework 2
– Stetige Weiterentwicklung
– Support durch Zend bis mindestens 2016
• Zend Framework 3
– Kommt in 2014
– Migration von ZF2 nach ZF3 deutlich einfacher
8. Probleme bei Migration
• Kein Tool für automatische Migration
• Kein Migration Layer (war mal versprochen)
• Migration-Guide in der ZF2 Dokumentation
fehlte lange und ist jetzt noch unvollständig
• ZF1 bietet viele Freiheiten & verlangt keine
100% feste Struktur wie andere Frameworks
• Allgemeingültige Schritt-für-Schritt-Anleitung
für alle Anwendungen schwer umsetzbar
9. Unterschiede ZF1 und ZF2 I
• Genutzte PHP Features
– Namespaces
– Closures
– Late Static Binding
• Git / GitHub
• Neue Konzepte
– »Echte« Modularität
– Dependency Injection / Service-Locator
– Event-Driven Architecture
10. Unterschiede ZF1 und ZF2 II
• ZF1: Convention over Configuration
• ZF2: Configuration over Convention
• Kein ZendRegistry im ZF2
• Kein ZendApplication im ZF2
• Zend_Controller → ZendMvc
• ZendForm: keine Dekorierer (ach menno ;-))
• ZendPdf und ZendService nicht im Core
• Viele Internationalisierungskomponenten
entfallen wegen Nutzung von ext/intl
11. Unterschiede ZF1 und ZF2 III
• Überarbeitete Komponenten
– ZendAuthentication, ZendDb, ZendForm,
ZendInputFilter, ZendMvc,
ZendPermissions, ZendView...
• Neue Komponenten
– ZendEventManager, ZendModuleManager,
ZendServiceManager, ZendDi, ZendStdlib...
• Ausgelagerte und entfallene Komponenten
– ZendDate, ZendDojo, ZendGdata, ZendLocale,
ZendPdf, ZendRegistry, ZendService...
12. »Einfache« Migration
• Zend Framework 1.11 oder 1.12 im Einsatz
• Empfohlene Projektstruktur wird eingehalten
• Anwendung in mehrere Module unterteilt
• Wenige eigene Framework-Erweiterungen
• View-Skripte verwenden PHP
• CSS und JS Frameworks beibehalten
• Thin Controllers / Fat Models
• Zend_Db_Table nur als Datenquelle verwendet
13. Komplexe Migration
• Zend Framework 1.8 bis 1.10 im Einsatz
• Projektstruktur teilweise eingehalten
• Anwendung unvollständig in Module unterteilt
• Einige wesentliche Framework-Erweiterungen
• View-Skripte mit gängiger Template-Engine
• CSS und JS Frameworks austauschen
• Medium Controllers / Medium Models
• Zend_Db_Table stark mit Methoden erweitert
14. Fiese Umsetzung
• Zend Framework 1.0 bis 1.7 im Einsatz
• Empfohlene Projektstruktur ignoriert
• Module gar nicht verwendet (Struktur)
• Sehr viele Framework-Erweiterungen
• View-Skripte setzen HTML_Template_IT ein
• Weder CSS noch JS Frameworks vorhanden
• Fat Controllers / No Models
• Zend_Db_Table wird nicht eingesetzt
15. Lösungsansätze
• ZF1 Anwendung in Namensräume portieren
– https://github.com/zendframework/Namespacer
– Viel manuelle Arbeit erforderlich
• ZF1 Library in ZF2 Applikation oder
ZF2 Library in ZF1 Applikation verwenden
– https://zf2.readthedocs.org/en/latest/
migration/zf1_zf2_parallel.html
• ZF1 Anwendung und ZF2 Anwendung
parallel laufen lassen
16. Vorgehensweise
• ZF1 Projekt vorbereiten
– »Default« Modul einrichten
– Verzeichnisse umbenennen
• ZF2 Projekt integrieren
– .htaccess Weiche für ZF1 und ZF2
• Routing beachten
• Startseite migrieren
• Nacheinander Module umziehen
20. Erste Vorbereitungen
• Backup des ZF1 Projektes (!)
• Start mit Kopie des gesamten Projektes
• Versionsverwaltung verwenden
– Fehlerhafte Schritte sind zurück nehmbar
– Branches für verschiedene Schritte denkbar
• Neue Datenbank oder Kopie erstellen
• Neuen Virtual Host einrichten
• PHP Upgrade auf mindestens PHP 5.3.3
• Projekt in IDE der Wahl einrichten
21. Modul »Default« einrichten
• Pfad /application/modules/default
• Klasse Default_Bootstrap erweitert
Zend_Application_Module_Bootstrap in
Datei Bootstrap.php
• Unter /application bleiben Verzeichnis
/modules und Datei Bootstrap.php übrig
• Alles andere ins »Default« Modul
• index.php anpassen für application.ini
im »Default« Modul
23. Konfiguration in application.ini
• Nicht mehr notwendig
– resources.frontController.controllerDirectory
• Pfade ggf. für »Default« Modul anpassen
– resources.layout.layoutPath
– resources.view.basePath
– resources.translate.content
• Aufnehmen, falls nicht vorhanden
– resources.modules[] = ""
• Einstellungen für Staging development,
production, usw. überprüfen
24. Verzeichnisse umbenennen
• Verzeichnisse umbenennen
– /application => /application.zf1
– /data => /data.zf1
– /library => /library.zf1
– /tests => /tests.zf1
• Konfiguration für Verzeichnisse anpassen
– APPLICATION_PATH in /public/index.php
– include_path in /public/index.php
– Pfade in application.ini
25. ZF1 Front-Controller-Datei
• Datei umbenennen in /public
– index.php => index.zf1.php
• Konfiguration in .htaccess anpassen
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.zf1.php
26. Anwendung prüfen!
• Läuft die Anwendung noch?
• Sind alle Module aufrufbar?
• Sind alle Seiten des »Default« Moduls
aufrufbar (Homepage, usw.)?
• Werden alle Seitenbereiche angezeigt?
• Was ist mit Übersetzungen?
• Macht PHP 5.3 Probleme?
• Erst wenn alles läuft, Migration fortsetzen!
28. Skeleton Application downloaden
• Als ZIP herunterladen und entpacken
– https://github.com/zendframework/
ZendSkeletonApplication
• Alle Dateien und Verzeichnisse ins ZF1
Projekt kopieren außer
– /public
• ZF2 installieren
– php composer.phar selfupdate
– php composer.phar install
29. ZF2 Front-Controller-Datei
• Aus SkeletonApplication ins ZF1 Projekt
– index.php => index.zf2.php
• PROJECT_PATH, APPLICATION_PATH und
APPLICATION_ENV definieren
• Konfiguration in .htaccess anpassen
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^application(.*)$ index.zf2.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.zf1.php
30. ZF2 Assets
• Asset Verzeichnisse ins ZF1 Projekt kopieren
– /public/css
– /public/img
– /public/js
• Ggf. auf Dateiüberschreibungen achten
– Verschiedene Twitter Bootstrap Versionen?
– Verschiedene jQuery Versionen?
– Grafiken wie favicon.ico
• style.css aus SkeletonApplication ignorieren
31. Anwendung prüfen!
• Prüfen, ob alle Seiten aus der alten ZF1
Anwendung korrekt angezeigt werden!
• Was passiert beim Aufruf der neuen URL
/application?
• Wird Twitter Bootstrap, jQuery geladen?
• Wenn alles klappt, wird ZF2 Skeleton
Application angezeigt
• => ZF1 und ZF2 laufen parallel
32. Vorteile dieses Ansatzes
• Das alte ZF1 Projekt kann parallel zu neuen
ZF2 Features betrieben werden
• Features können schrittweise von ZF1 nach
ZF2 portiert werden
• Wenn alle alten ZF1 Features portiert sind,
können .zf1 Verzeichnisse entfernt werden
33. Nachteile dieses Ansatzes
• Layouts sind unabhängig
– Lösung: Layouts duplizieren
• Konfigurierbare Kombination einer Navigation
aus ZF1 und ZF2 nur schwer möglich
– Lösung: Navigation duplizieren
• Ist Nutzer in ZF1 angemeldet, ist geschützter
Zugriff auf ZF2 Module erschwert
– Lösung: Sessions in Datenbank speichern
34. Routing
• Überschneidungen beim Routing vermeiden!
• Neue ZF2 Features bekommen neue Routen
• Vorhandene ZF1 Routen wiederverwenden,
wenn ZF2 Modul ein altes ZF1 Modul ersetzt
• Im ZF2 keine »globalen« Routen verwenden
• Starter für neue ZF2 Routen müssen in
der .htaccess notiert werden
36. .htaccess anpassen
• Anfragen für Homepage auf index.zf2.php
umleiten
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^$|^en$ index.zf2.php
RewriteRule ^application(.*)$ index.zf2.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* start.zf1.php
• Testen!
37. Layout migrieren
• Aus dem ZF1 ins ZF2 kopieren
• Veränderte View-Helper anpassen
• Fehlende View-Helper auskommentieren
• Layout hierarchisch?
– Header, Footer, Sidebar
– Umsetzung z.B. mit ApplicationListener
• SkeletonApplication Assets entfernen
– CSS Datei style.css & Grafik zfs-logo.png
• Testen!
38. Navigation migrieren
• ZF1 Navigation
– /application.zf1/modules/default/configs/
application.ini
• ZF2 Navigation konfigurieren
– /module/Application/config/module.config.php
– Service nicht vergessen!
• Links zur ZF1 Anwendung als URI definieren
– Nach Migration der Module ändern
• Partial für Ausgabe im Twitter Bootstrap Style
• Testen!
39. Translator migrieren I
• Dateien der SkeletonApplication löschen
– /module/Application/language/*
• Übersetzungsdateien kopieren
– Von /modules/default/locales/*.php
– Nach /module/Application/language/
• Konfiguration in module.config.php ändern
• Testen!
40. Translator migrieren II
• Fehlende Module unter /module anlegen
– /module/ModulName/config/
module.config.php => translator
– /module/ModulName/language/
– /module/ModulName/Module.php : getConfig()
• Übersetzungsdateien kopieren
– Für jedes Modul
• Module aktivieren
– /config/application.config.php
• Testen!
41. Autoloading für ZF1 Dateien I
• ClassMap für ZF1 Module erstellen
– in /application.zf1/modules
• Konfiguration im Application Modul
– Module::getAutoloaderConfig()
• Wahlweise
– include_path in /public/index.php ergänzen
mit PROJECT_ROOT . '/library.zf1'
– Alle require_once Statements aus den ZF1
Library Dateien entfernen
43. ZF1 Model-Service in ZF2 nutzen
• IndexController in Application Modul
greift auf Blog_Service_Article zu
• Klasse sollte gefunden werden
• Konfigurationsprobleme
– cache nicht in Zend_Registry vorhanden
– Kein Default Adapter für Zend_Db_Table gesetzt
• Lösung:
– CompatibilityListener einrichten, um
Konfigurationsprobleme zu lösen
45. Startseiten View-Skript migrieren I
• View-Skript kopieren
– Von /application.zf1/modules/default/
views/scripts/index/index.phtml
– Nach /module/Application/view/
application/index/index.phtml
• Probleme mit View-Helpern
– URL View-Helper fehlen Routen
– URL View-Helper andere Parameter-Reihenfolge
– View-Helper Date und Time fehlen
46. Startseiten View-Skript migrieren II
• Eigenen Date View-Helper einrichten
– Zur Ausgabe von Datumswerte
– Konfiguration für Service-Manager
• Alternative
– DateFormat View-Helper
• Testen!
47. Offene Punkte
• Navigation zeigt alle Menüpunkte, da noch
keine ACL eingerichtet ist
• User Widget fehlt noch in der Seitenleiste
• Modul User migrieren
• Modul Blog migrieren
49. Zusammenfassung
• ZF2 Anwendung kann ZF1 Klassen
verwenden
• Layout, Navigation, Translator und View-
Skript wurden migriert
• CompatibilityListener für Migration von
Konfigurationseinstellungen
• ZF1 Module wurden zum Teil in ZF2
Anwendung eingerichtet
• Startseite nutzt Blog_Service_Article
51. Routing migrieren I
• Anfragen für Blog Modul in .htaccess
umleiten
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^$|^en$ index.zf2.php
RewriteRule ^application(.*)$ index.zf2.php
RewriteRule ^(de|en)/blog|beitrag|kategorie
|nutzer(.*)$ index.zf2.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* start.zf1.php
52. Routing migrieren II
• Jedes ZF2 Modul sollte immer mindestens
eine eigene Route haben
– Nicht auf eine globale Route verlassen!
• Es werden vier Routen benötigt
– Generelle Route für Blog Modul
– Spezielle Route für Blogbeiträge
– Spezielle Route für Beiträge einer Blogkategorie
– Spezielle Route für Beiträge eines Nutzers
53. Navigation migrieren
• Navigation-Konfiguration aus Application
Modul ins Blog Modul verschieben
• Seitentyp von uri auf mvc wechseln
• Reihenfolge der Menüpunkte durch
Parameter order festlegen
54. Controller migrieren I
• Controller in module.config.php
konfigurieren und mit Routen matchen
• Verzeichnis anlegen
– /module/Blog/src/Blog/Controller
• Controller mit benötigten Methoden anlegen
– IndexController mit indexAction(),
showAction(), categoryAction(), usw.
– AdminController
– CategoryController
– usw.
55. Controller migrieren II
• Code der Aktionsmethoden aus ZF1 Modul
übernehmen und anpassen
– Parameter holen mit params() Controller-Plugin
– Umleitungen für redirect() Plugin anpassen
– URL-Generierung für url() Plugin anpassen
– Templatevariablen an ViewModel übergeben
• Verwendung der ZF1 Model-Services kann
vorerst beibehalten werden
56. View-Skripte migrieren
• View-Manager konfigurieren, damit View-
Skripte gefunden werden können
• Verzeichnisse anlegen
– /module/Blog/view/blog/index/
– /module/Blog/view/blog/admin/
– /module/Blog/view/blog/category/
• Code der View-Skripte aus ZF1 Modul
übernehmen und anpassen
– Einsatz der Standard View-Helper überprüfen
– Eigene View-Helper migrieren
57. View-Helper migrieren
• View-Helper im Modul Blog konfigurieren,
damit sie gefunden werden
• Im View-Helper darauf achten:
– Statt $this->view wie im ZF1 muss im ZF2
$this->getView() verwendet werden
– Auf Parameter der Standard View-Helper achten
• Url, Translate, usw.
• Anwendung testen!
58. Model-Layer & Forms übernehmen
• Im Administrationsbereich werden vorerst
folgende ZF1 Klassen weiterverwendet
– Model-Layer mit Entities, Datenquellen, Mapper
und Model-Service
– Formulare mit Filtern und Validatoren
• Im CompatibilityListener konfigurieren
– Zend_Controller_Front benötigt ggf. Instanzen
vom Request und Response Objekt
– Zend_Translate zur Verwendung mit Zend_Form
– Namensräume für eigene Filter und Validatoren
59. CompatibilityListener (Auszug) I
namespace ApplicationListener;
class CompatibilityListener implements ListenerAggregateInterface
{
public function setupZF1Compatibility(EventInterface $e)
{
Zend_Controller_Front::getInstance()
->setRequest(new Zend_Controller_Request_Http())
->setResponse(new Zend_Controller_Response_Http())
->setDispatcher(new Zend_Controller_Dispatcher_Standard());
$translations = array();
$applicationConfig = $e->getApplication()->getServiceManager()
->get('ApplicationConfig');
foreach ($applicationConfig['modules'] as $module) {
$localesFile = PROJECT_PATH . '/module/' . $module . '/language/'
. $e->getRouteMatch()->getParam('lang') . '.php';
if (file_exists($localesFile)) {
$translations = array_merge($translations, include $localesFile);
ksort($translations);
}
}
[...]
61. Weitere Migrationen
• AdminController zum Pflegen der
Blogbeiträge inkl. der View-Skripts
• CategoryController zum Pflegen der
Blogkategorien inkl. der View-Skripts
• ZF1 Formulare benötigen für Ausgabe eine
Zend_View Instanz!
• Testen!
62. Zusammenfassung
• Blog Modul läuft auf ZF2 mit einigen
Einschränkungen
– Verwendet noch ZF1 Model-Layer
– Verwendet noch ZF1 Formulare
– Verwendet noch ZF1 Filter und Validatoren
– Ist abhängig vom CompatibilityListener
64. User-Widget migrieren
• View-Helper migrieren
• Anfragen zum Einloggen auf ZF1 User Modul
umleiten
• View-Helper konfigurieren
• UserWidget in Seitenleiste ausgeben
66. CompatibilityListener (Auszug)
namespace ApplicationListener;
class CompatibilityListener implements ListenerAggregateInterface
{
public function setupZF1Compatibility(EventInterface $e)
{
[...]
$sessionOptions = $config->resources->session->toArray();
$saveHandlerClass = $sessionOptions['saveHandler']['class'];
$saveHandler = new $saveHandlerClass(
$sessionOptions['saveHandler']['options']
);
unset($sessionOptions['saveHandler']);
Zend_Session::setOptions($sessionOptions);
Zend_Session::setSaveHandler($saveHandler);
$e->getApplication()->getServiceManager()
->setService('UserAuthentication', Zend_Auth::getInstance());
}
}
67. Autorisierung
• Konfiguration der Benutzerrechte in den
module.config.php Dateien der Module
• Erstellung von UserAclAcl, die ZendAcl
erweitert und ACL-Konfiguration verarbeitet
• Factory für UserAclAcl
• AuthorizationListener zum Prüfen der
Benutzerrechte mit ACL und der Rolle
– Bei fehlenden Rechten Umleitung auf forbidden
Seite des ZF1 User Moduls
68. ACL mit Navigation kombinieren
• Konfiguration der Navigation mit Parametern
resource und privilege erweitern
• Navigation-Konfiguration auf MVC Seiten
umstellen
• Navigation für User Modul ins Modul
• Erweiterung vom AuthorizationListener
zur Übergabe der aktuellen Zend_Auth Rolle
sowie ACL an Navigation View-Helper
69. Zusammenfassung
• ZF2 der Anwendung nutzt Authentifizierung
des ZF1 User Moduls
• ZF2 der Anwendung stellt Autorisierung
selber bereit (wegen ZendNavigation)
• User-Widget leitet entsprechend auf ZF1
User Modul um
• Später kann gesamtes User Modul migriert
werden
71. Model-Entities
• Model-Entities nahezu 1:1 migrierbar
• Umschreibung auf Namensräume sinnvoll
• ZendStdlibArraySerializableInterface
– Implementation sinnvoll
– Für Einsatz mit Hydratoren
• Instanzierung über Service-Manager
72. Table-Gateways
• ZendDbTableGatewayTableGateway als
Datenquelle implementieren
• Einsatz eines ResultSets mit Hydratoren,
um Instanzen der Model-Entities zu erhalten
• Abfragen von Daten als Methoden mithilfe
von ZendDbSqlSql implementieren
• Ansonsten nicht zu viel Logik implementieren
• Instanzierung über Service-Manager
73. Mapper und Hydratoren I
• Aufgaben des Mappers
– Übergabe Arraydaten an Objekt und umgekehrt
– Aufbereitung Daten zum Schreiben in Datenbank
– Methoden zum Lesen der Daten
• Mapper auflösen
– Aufbereitung der Daten zum Schreiben
→ Model-Service
– Methoden zum Lesen der Daten
→ TableGateway
74. Mapper und Hydratoren II
• Hydrator kann Eigenschaften eines Objektes
mit Arraydaten befüllen und umgekehrt
– Datenbank → Objekt
– Objekt → Datenbank
– Formulardaten → Objekt
• Beispiele für Hydratoren
– ZendStdlibHydratorArraySerializable
– ZendStdlibHydratorClassMethods
– ZendStdlibHydratorReflection ...
• Einsatz des Hydrator-Service-Managers
75. Model-Services
• Model-Services nahezu 1:1 migrierbar
– Verknüpfung zum Mapper nicht notwendig
– Stattdessen Abfragen direkt an TableGateway
– Methoden create(), update() und delete()
bereiten Daten für TableGateway vor
– Ggf. Einsatz von ZendPaginator sinnvoll, da mit
Model-Entities kombinierbar
• Ging im ZF1 nicht ohne weiteres
• Instanzierung über Service-Manager
76. Formulare
• Große Unterschiede zwischen ZF1 und ZF2
– Keine Dekoratoren (schade!)
– Ausgabe per View-Helper
– Kombination mit Input-Filtern, Hydratoren und
Model-Entities möglich
• Alle Formulare von Grund auf neu aufbauen!
• Dazu Input-Filter erstellen, die auch
unabhängig vom Formular verwendbar sind
• Instanzierung über Service-Manager
77. Fazit
• Selbst für eine gut vorbereitete ZF1
Anwendung ist Migration zum ZF2 aufwändig
• Bei komplexeren Anwendungen ist ein
Schritt-für-Schritt Neubau meist sinnvoll
• Tipp:
– Nicht als erstes ZF2 Projekt ein ZF1 migrieren
– Stattdessen ein neues ZF2 Projekt aufbauen, um
sich einzuarbeiten
78. Vielen Dank für
Ihre Aufmerksamkeit!
Ralf Eggert
https://profiles.google.com/eggert.ralf