Archiv der Kategorie: Zend Framework

Zend Framework, Windows und das Model

Wer unter Windows entwickelt hat es oft nicht leicht, obwohl es eigentlich unter Windows alles leichter sein sollte. Aber gerade diese „alles ist leichter“ Sachen machen es einem Entwickler manchmal schwer. Schwer, den Fehler zu finden.

Beim Zend Framework (v1.x) hat man gewisse Namenskonventionen einzuhalten. Ist einfach so. Eine dieser Konventionen betrifft die Möglichkeit, Klassen automatisch zu laden. Dazu müssen diese einem bestimmten Namensmuster folgen und in einem bestimmten Verzeichnis liegen. Der Entwickler spart sich dann das lästige „require…„. Nettes Feature.

Unter Windows aber bitte aufpassen!
Schnell kommt es zu einem „Fatal Error„, weil die Datei nicht gefunden werden konnte, obwohl diese eigentlich im richtigen Verzeichnis liegt. Und im lokalen Development-System funktioniert doch alles. Server kaputt? Mitnichten!

Aufbasse: Unter Windows ist es völlig egal, welchen „case“ die Verzeichnisse und Dateinamen haben. Ein Verzeichnis „model“ ist für Windows das gleiche wie ein Verzeichnis „Model„. Das ZF würde in einem Windows-Server in beiden Fällen problemlos dein Model finden (mal vorausgesetzt, der Rest entspricht den Namenskonventionen), ein Linux-Server (und das ist der Normalfall) findet bei „model“ dein Model aber eben nicht.

Lösung: Nenn das VZ „Model„, lösch das alte VZ „model“ auf dem Server und achte nächstes Mal gut darauf. Vor allem du, Sascha 😉

Automatische Validierung mit dem W3C-Validator

Das W3C stellt eine sehr elegante API bereit, mit deren Hilfe man recht einfach Seiten automatisiert überprüfen kann, ob die Seiten Fehler hat und wenn ja, welche das sind.

Ich stelle das mal mit Hilfe des Zend Frameworks vor, da es damit wirklich sehr trivial ist.

<?php
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();

$client = new Zend_Http_Client();
$result = $client->setMethod(Zend_Http_Client::POST)
->setUri('http://validator.w3.org/check')
->setParameterPost('uri', 'http://www.codemercenary.de')
->request();

Zend_Debug::dump($result->getHeaders());
file_put_contents('w3cresult.html', $result->getBody());

Als Ausgabe erhält nun die Header

array(12) {
'Date' =>
string(29) "Thu, 11 Oct 2012 12:03:20 GMT"
'Server' =>
string(22) "Apache/2.2.16 (Debian)"
'Content-language' =>
string(2) "en"
'X-w3c-validator-recursion' =>
string(1) "1"
'X-w3c-validator-status' =>
string(7) "Invalid"
'X-w3c-validator-errors' =>
string(2) "48"
'X-w3c-validator-warnings' =>
string(1) "3"
'Vary' =>
string(15) "Accept-Encoding"
'Content-encoding' =>
string(4) "gzip"
'Content-length' =>
string(4) "7228"
'Content-type' =>
string(24) "text/html; charset=UTF-8"
'Connection' =>
string(5) "close"
}

Die Header kann nun recht einfach vergleichen, bei „X-w3c-validator-status“ kann man mittels test auf „Invalid“ schnell feststellen, dass was nicht funktioniert hat. Eine kurze Übersicht über die Anzahl der Fehler und Warnungen ist auch mittels Headeranalyse möglich.

Weitere Details stehen dann im Body, den ich im Beispiel in der letzten Zeile mal eben schnell auf die lokale Festplatte downloade.

So, das war der Crashkurs zum Thema „W3C-Validierung-Prüfung mit PHP“, weitere Infos findet ihr beim W3C in der API Doku: http://validator.w3.org/docs/api.html

Zend Framework im Zend Server CE updaten

Der aktuelle ZendServer 5.6 kommt mit vorinstalliertem ZendFramework 1.11.11.
Dies ist aber veraltet, eine automatische Aktualisierung gibt es leider nicht; allerdings ist der Update-Vorgang nicht so schwer.

Von der Zend Framework Download Seite holt ihr euch das aktuelle Zend Framework (bei mir 1.11.13 minimal) und entpackt dieses irgendwohin. Von diesen entpackten Dateien braucht ihr nun das Verzeichnis „Zend„, welches ihr in „/library“ findet.

Das kopiert ihr euch in den Pfad „ZendServer\share\ZendFramework\library“ eures ZendServers, bei mir ist das „C:\zend\ZendServer\share\ZendFramework\library“ (yep, immer noch Windows).

Der Server benötigt keinen Neustart und ihr könnt nun direkt die neue Version benutzen.
Viel Erfolg!

No reference from table X to table Y

Referenzen der Datenbank im Zend Framework über das Model abzubilden ist ja eine gute Idee. Schnell ist das entsprechende Skelett des Model erstellt und die Doku lehrt uns: „Definier die $_referenceMap und alles wird gut.“. Dachte ich auch … aber

Wer das schon einmal ausprobiert hat, der weiß: Im aber steckt der Wurm im Apfel bzw. die Titelmeldung auf dem Schirm. Aber langsam und zum mitmachen:

Gegeben sind 2 Datenbanktabellen „news“ und „author“, news hat u.a. ein Feld authorId. Den Rest könnt ihr euch vorstellen.

class myApp_Model_News extends Zend_Db_Table_Abstract {

protected $_name = 'news';
protected $_primary = 'id';

protected $_referenceMap = array(

'TrouperId' => array(

'columns' => array('author_id'),
'refTableClass' => 'author',
'refColumns' => array('id')

)

);

}

Instinktiv benutzt man das und hier meldet sich auch gleich eine Fehlermeldung:
No reference from table myApp_Model_News to table myApp_Model_Author
Aber warum? Wir haben doch alles richtig gemacht.

Prinzipiell ja, laut Doku, aber nicht laut programmiertem Code, dafür muss man nämlich nicht den Tabellennamen in die $_referenceMap eintragen, sondern den Namen des Models!!!
Also so:

class myApp_Model_News extends Zend_Db_Table_Abstract {

protected $_name = 'news';
protected $_primary = 'id';

protected $_referenceMap = array(

'TrouperId' => array(

'columns' => array('author_id'),
'refTableClass' => 'myApp_Model_Author',
'refColumns' => array('id')

)

);

}

Besonders mal auf „refTableClass“ sehen, dort steht nun nicht mehr der Tabellenname wie er in der Datenbank steht, sondern der Name des Models, dass für diese Tabelle zuständig ist.
Und nun funktioniert das ganze auch mit

$news = new myApp_Model_News();
$myNews = $news->fetchRow();
$newsauthor = $myNews->findDependentRowset('myApp_Model_Author');

Mehrzeiliger Text mit Zend_Pdf

Mehrzeiligen Text erstellen muss man auch mit Zend_Pdf noch manuell … leider
Ich möchte an dieser Stelle allen, die mehrzeiligen Text in einem PDF mit Hilfe von Zend_Pdf ausgeben wollen, einen Denkanstoß geben.

Ich habe das Problem wie folgt gelöst:

  1. Verfügbare Breite ermitteln. Diese Breite in einer Variable merken.
  2. Verfügbare Zeilen ermitteln. Diese Info auch speichern, da das für den späteren Seitenumbruch nötig wird. Nicht, dass euer Text plötzlich endet, nur weil die Seite zu Ende ist.
  3. Den Text mittels wordwrap umbrechen. 
  4. Diesen dann per explode in ein array bekommen.
  5. Das dann per foreach und array_shift solange ausgelesen wird, bis das Seitenende erreicht ist. 
  6. Dannach eine neue Seite beginnen und den Rest des arrays abarbeiten.
Ich hoffe, ich kann euch zumindest auf die richtige Spur bringen, dass das ganze gar nicht schwer ist. Am Ende könnt ihr beliebig großen Text in einem PDF darstellen und das sogar über mehrere Seiten.
Viel Erfolg.

Das Framework updaten

Wer kennt das nicht? Man betreut eine Vielzahl unterschiedlicher Projekte. Im Idealfall betreut man nur wenige und kennt diese fast auswendig … aber wer von uns lebt schon im Idealfall? Eben …

So ein PHP-Projekt ist eine komplizierte Sache und auch die „kleinen“ bestehen aus einer Vielzahl unterschiedlicher Technologien. Zum einen (meist) ein Framework als Basis, sei es das Zend Framework oder Symphony oder CakePHP oder CodeIgniter oder oder oder, dann – bei den neueren und „cooleren“ Projekten – eine Datenbankabtraktionsschicht (ich kenne das z.B. Doctrine, aber es gibt da auch ein paar mehr) und dann kommt die Schicht Code, die uns dann die grauen Haare machen, die Anwendungsschicht, so nenne ich nun mal den Teil des Codes, den man bei einem Projekt mit Framework-Basis selbst schreibt.

Nach vielen Tagen ist der Code fertig, alle Tests zeigen „Grün“, das Deployment war (außnahmsweise mal) angenehm einfach und alles ist perfekt. Der Kunde ist zufrieden, der Zeit- und Projektplan eingehalten, der Chef spendiert ne Pizza und nach vielen Wochen hat man endlich mal wieder einen der wenigen „Geschafft“-Momente, die einem wieder Rückenwind geben. Toll.

Nach ein paar Wochen kommt dann aber schon die erste Verbesserung, der erste Bugfix, der erste neue Kundenwunsch. Die perfekte Anwendung muss aktualisiert werden. Im Bereich der Anwendungsschicht meist ein leichtes. Käfer erschlagen, Feature eincoden, Test, VCS, Deploy, „der nächste bitte“; das war einfach.

Allerdings kommt es irgendwann und du kannst dich nicht davor verstecken. „You can run, but you can’t hide!“ (gibt es so ein YCRBYCH-Prinzip eigentlich irgendwo? Ich habe da nichts finden können.). Das Framework, die Basis der Anwendung wird (meist von extern) aktualisiert. Und nun?

Meistens hält man sich ja auf dem Laufenden und schaut nach, was es denn neues gibt. „Ah ja, Aha, soso, benutzen wir ja gar nicht, brauchen wir nicht updaten.“ Okay, kann passieren, aber, wie schon gesagt, YCRBYCH – das Changelog zeigt nun sehr viele Änderungen, Bugfixes, Performance-Improvements usw. genau in den Klassen und Bereichen an, die in der Kundenanwendung benutzt werden. Und nun der Worst-Case: Bugfixes zu Exploits sind auch dabei. Es heißt also: Das Framework updaten!

Und nun? Nun, sicher, Framework auf dem lokalen System updaten, testen, Testsystem updaten, testen usw. Aber eines musst du zugeben: Du hast ein mulmiges Gefühl dabei! Es könnte was schiefgehen. Klar, eigentlich kann nichts schiefgehen, aber es könnte doch sein … und dann?

An dem Punkt dachte ich dann über folgendes nach: Warum können wir mit einem Framework nicht machen, was ein Browser heute schon kann (und andere Software eigentlich auch können sollte): Silent-Update! Also, ein Update installieren ohne dass was schiefgeht? Ganz einfach: Weil dann etwas schiefgehen wird, Murphy’s Law, kennt jeder.

Nach tausend „ja, aber…“ Gedanken machte es dann bei mir *klick* (ich möchte jetzt keinen „Schnellmerker“ Kommentar hören!) … sowas wie „Silent-Updates“ machen wir doch … nur eben nicht wir Entwickler bei uns selbst, sondern wir Entwickler beim Kunden. Sobald die Anwendung getestet und für gut befunden ist wird diese meist auch deployed (und ich hoffe mal, dass die Anwendung dann immer noch funktioniert) und dies in der Regel ohne das der Kunden nochmal dazu extra ein „Ja, updaten“ Knopf drücken muss.

Aber kann man einen PHP-Code nicht auch so schreiben, dass man das zugrunde liegende Framework jederzeit gegen eine neuere Version eintauschen könnte? Ich setze vorraus, dass keine Architektonischen Änderungen am Framework vorgenommen werden, dass also das Framework „manual-compatible“ ist. Wie ist deine Meinung dazu? Wie müsste der Code dazu aussehen? Kann man solchen Code überhaupt schreiben? Oder ist das Utopie?

Fatal Error im neuen Zend Framework Projekt

Ich setze grad eine neues Zend Framework Projekt im Zend Studio auf, vergebe Namen und Ort, Zend Studio macht auch einiges und *plop* ein neues Projekt ist geboren.

Zend Server und hosts noch einstellen und ein kleiner Test im Browser sagt mir: „Ja, alles okay, Projekt kann starten!“

Aber schon nach den ersten paar Zeilen im index Controller gehts los:

Fatal error: Cannot redeclare class zend_loader. If this code worked without the Zend Optimizer+, please set zend_optimizerplus.dups_fix=1 in your php.ini in C:\zend\Apache2\htdocs\blahblah\library\Zend\Loader\PluginLoader.php on line 27

Nach langem Suchen: Die Lösung findet sich in der /public/index.php, dort diese Zeilen auskommentieren:

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));

Dann funktionieren auch wieder alle Aufrufe 😉

Method-Chaining in eigenen PHP-Projekten einsetzen

Klassen und Objekte kennt ja nun mittlerweile fast jeder, ein PHP-Framework wie ZendFramework oder CodeIgniter haben sich sicherlich auch schon viele zumindest mal angesehen … will ich hoffen. Nun, auch wenn man die Frameworks nicht direkt einsetzt, so kann man doch viel von ihnen lernen. Viele der Techniken, die in bekannten PHP-Frameworks eingesetzt werden, entstanden entweder aus dem großen Ästhetischen Faible, den nun mar jeder Entwickler hat oder schlicht aus Faulheit der Programmierer 😉

Egal, eines der – wie ich finde – tollen Features in vielen Frameworks ist die Möglichkeit, mehrere Funktionen eines Objektes hintereinander aufrufen zu können, ja, es sieht schon fast wie ein normaler Satz aus, was dann im Editor steht und seinen Dienst verrichtet. Diese Möglichkeit der sog. “Fluent Interfaces” nennt man “Method-Chaining” (und wieder im Bullshot-Bingo gewonnen, *strike*).

Kleines Beispiel, kennen wir alle, unsere Basisklasse:

class Base1 {
function macheEins() {
// some magic here
}
function macheZwei($parameter) {
// more magic here
}
}

Das ganze rufe ich nun auf:

$myBaseClass = new Base1();
$result1 = $myBaseClass->macheEins();
$result2 = $myBaseClass->macheZwei($result1);

Was passiert? Das Ergebnis des Aufrufs von “macheEins” ist der Parameter für “macheZwei”. Das ganze sieht strukturiert aus und ist lesbar, was also soll man besser machen können? Nein! Bitte jetzt nicht sagen, man könne doch sowas machen:

$myBaseClass = new Base1();
$result2 = $myBaseClass->macheZwei($myBaseClass->macheEins());

Sicher, es funktioniert, aber: NEIN! Macht das nicht! Warum? Ganz einfach: Debugge das mal, viel Spaß. Denn wenn man erst einmal mit so einem falschen Verhalten anfängt, dann verschachteln sich schnell auch mal 5 oder 8 Funktionen ineinander und finde dann mal den Fehler, viel Spaß! Also: Ganz klares “So nicht!”.

Aber wie dann?

Zunächst müssen uns im klaren sein, was die Klasse macht. Offenbar braucht “macheZwei” ein Ergebnis einer Berechnung einer anderen Funktion der Klasse. Dieses Ergebnis könnte man doch genauso innerhalb der Klasse speichern und dann benutzen.

class Base2 {
private $valueHolder;
function macheEins() {
$this->valueHolder = someMagic;
return $this->valueHolder;
}
function macheZwei() {
return $this->valueHolder * someMoreMagic;
}
}

Schon erfüllt die Klasse auch die Anforderungen, aber … so richtig “fluent” will das ganze nicht werden, obwohl ja nun im Aufruf von “macheZwei” der Übergabeparameter fehlt. Was nun? Und wie sieht denn so ein “Fluent Interface” mit “Method-Chaining” nun aus? Der Aufruf sähe in unserem Beispiel in etwa so aus:

$myBaseClass = new BaseFluent();
$result2 = $myBaseClass->macheEins()->macheZwei();

Aber unsere derzeitige Klasse unterstützt das nicht! Wie bekommen wir unsere Klasse nun “Fluent”?

Nun, dazu müssen wir diese massiv umbauen. Der größte Umbau ist, dass die einzelnen Methoden nicht mehr direkt die Ergebnisse liefern, sondern “nur” das Objekt selbst zurückgeben … und darin liegt auch schon der ganze Trick. Den Methodenaufruf kann ich immer nur auf einem Objekt machen. Eine Methode, die mir einen Basisdatentypen zurück liefert, kann ich dafür nicht gebrauchen, da ich auf diesem Basistypen (int, String, array, …) keine weiteren Methoden meiner Klasse aufrufen kann.

Nehmen wir mal an, im letzten Code stünde statt “new BaseFluent” ein “new Base2”. Dann würde der Aufruf:

$result2 = $myBaseClass->macheEins()->macheZwei();

folgendes bedeuten: Rufe die Methode “macheEins” auf dem Objekt “myBaseClass” auf, diese gibt den Datentyp von valueHolder zurück (nehmen wir mal an, es wäre ein integer mit dem rein zufälligen Wert 42), rufe dann auf dem Objekt 42 die Methode “macheZwei” auf … *meep* Fehlermeldung, “42” ist kein Objekt, hat daher generell keine Methoden und erst recht keine spezielle Methode mit dem Namen “macheZwei” also Fehler und Script Abbruch.

Der Trick besteht nun darin, dass die Methoden des Objektes nicht mehr die eigentlichen Ergebnisse zurückgeben, sondern das Objekt der Klasse selbst; darauf darf man ja dann auch wieder Methoden derselben Klasse aufrufen, also bauen wir flugs die Klasse um:

class BaseFluent {
private $valueHolder;
function macheEins() {
$this->valueHolder = someMagic;
return $this;
}
function macheZwei() {
$this->valueHolder *= someMoreMagic;
return $this;
}
function getValueHolder() {
return $this->valueHolder;
}
}

Man erkennt nun, wohin die Reise geht. Getter und Setter werden implementiert, um die Daten zu holen, die Methoden geben uns $this zurück, worauf wir weiterhin Klassenmethoden aufrufen können und wir haben nun unser “Fluent Interface” für “Method-Chaining” in PHP realisiert; war doch gar nicht schwer und hat auch gar nicht weh getan, oder?

In der freien Wildbahn trefft ihr auf diese Art des Codens übrigens ganz stark beim ZendFramework an, bei CodeIgniter geht es wohl auch, denke ich (ich mache mich da erst seit kurzem fit und bitte alle CI-Fans, meine Unwissenheit zu entschuldigen). Es macht aber auch Spaß, dass bei eigenen Klassen umzusetzen, die sowieso umgearbeitet werden sollen. Sieht einfach viel übersichtlicher aus. Und noch ein Tipp: Mehr als zwei Verkettungen sollten untereinander stehen, also so:


$myBaseClass->macheEins()
->macheZwei()
->macheDrei()
->undNochMehr();

Viel Erfolg damit …

ISO 3166 Code für eine Benutzereingaben finden … mit dem Zend Framework

Stichwort Benutzer-Regisitrierung mit Angabe des Landes: Der User gibt ein „Deutschland“, ist ja auch okay so, aber eure Anwendung (oder eine Drittanwendung) braucht den ISO Code dazu. Und was ist mit Schweiz oder einem Land wie Mexico?

Was nun? Eine lange Tabelle pflegen, womöglich noch selber? Das geht heute viel einfacher, zumindest, wenn man das Zend Framework benutzt:


$locale = new Zend_Locale('DE');
$list = $locale->getTranslationList('territory', 'de_de');
$filterlist = array_search('Deutschland', $list);
echo $filterlist;

Der Trick ist nun, dass wir uns eine Liste aller Länder mit den Codes ausgeben lassen, diese Liste ist in der Sprache, in der die Besucher auch die Daten eingeben (hier Deutsch). Nun suchen wir mit array_search() einfach den richtigen Wert raus (oder reagieren mit einem Fehler auf nicht gefunden, das ist hier nicht mit drin).

Die Ausgabe lautet richtigigerweise DE.

Datenbankverbindung beim Zend Framework auf UTF-8 umstellen

Bei verwendung des Zend Frameworks und dessen application.ini ist es sehr leicht, den gewünschten Zeichensatz einzustellen, damit sowas nicht passiert: 40,8�C

Ulrich hat da schon 2009 einen Artikel drüber geschrieben, an dieser Stelle nochmal: Danke!

Hier die Lösung: In eure application.ini tragt ihr einfach den entsprechenden charset-parameter ein:

resources.db.params.charset  = utf8

Und schon habt ihr “saubere” Ergebnisse: 40,8°C