Archiv der Kategorie: PHP

webEdition: Bestehende Datenbankverbindung nutzen

In webEdition kann man viel machen, es ermöglicht einem wirklich sehr große Freiheit. Der große Vorteil diese Freiheit hat aber auch einen großen Nachteil: Man muss sich im System auskennen, um damit wirklich gute Seiten bauen zu können; ganz schnell kann man auch sehr langsame Seiten erstellen, die dann nicht nur den Besucher, sondern vor allem den Kunden verärgern.

Aus einem aktuellen Projekt stelle ich eine wirklich böse Fehlerquelle vor: Die mehrfache Datenbank Verbindung. Im Template ist es recht einfach, eine neue DB-Verbindung mit der bekannten (und veralteten) Funktion

$db = mysql_connect(...);

zu erstellen. Aber warum sollte man das machen? Wohl nur aus Unwissenheit, dass webEdition bereits eine Datenbankverbindung eröffnet hat und diese auch dem Entwickler bereitstellt.

Also, liebe webEdition-Entwickler, die bestehende, persistente und performantere DB-Verbindung könnt ihr recht einfach für eigene Zwecke benutzen:

$db = $GLOBALS['DB_WE'];
$stmt = $db->query('SELECT * FROM tblUser');
while ($data = mysql_fetch_assoc($stmt)) {
Zend_Debug::dump($data);
}

Muss man dann doch mal eine zweite Verbindung aufbauen – was im Einzelfall manchmal wirklich sein muss – dann ist dem Entwickler ja meist bewusst, was er da macht und ich hoffe, er benutzt dann dafür nicht die alten, langsamen MySQL-Funktionen, sondern entweder die MySQLi-Pendants oder eine PDO-Schnittstelle.

Leider stellt webEdition keine Instanz von Zend_Db bereit, so dass zwar die Generierung des Querys OOP stattfindet, aber es dannach mit den bekannten mysql-Funktinen weitergeht. Das ist ein recht großer Nachteil, da es die Möglichkeit nimmt, auf einem modernen (aktuellen) Niveau zu arbeiten und ich hoffe, dass dieses Manko bald durch ein aktuelles Release behoben wird.

include oder require?

Was soll der PHP-Entwickler benutzen, include oder require bzw. include_once oder require_once?
Und wo liegt da eigentlich der Unterschied?

Die Frage kommt oft, deshalb an dieser Stelle mal ganz klipp und klar und kurz:

require bzw. require_once benutzen, denn: Sowohl include wie auch require binden eine Datei ein, aber, sollte ein Fehler in der includierten Dateie sein, so bricht require mit einem E_COMPILE_ERROR ab, während include fröhlich mit einer WARNING weitermacht.

Im Sinne der Vermeidung von code-smells fällt eure Wahl also auf require bzw. require_once.

Schneller in PHP und MySQL mit JOIN

Mittels einfacher Kentnisse seiner Datenbank kann der Entwickler auch aus Legacy-Anwendungen sehr viel mehr Geschwindigkeit herausholen, als er vielleicht weiß.
Der Grund ist simpel: Meist bleibt der Code gleich, aber die Server Software wird aktualisiert. Während die Erstellung noch im guten alten PHP4 + MySQL4 von Statten ging, rennt der Code heute mit PHP5 + MySQL5 zwar immer noch, könnte aber dank kleiner Kniffe sehr viel schneller sein.
Ich möchte euch einen Weg dazu zeigen, die JOINs in MySQL. Es gibt sicherlich noch mehr Möglichkeiten, aber das sind auch andere Themen. Fangen wir heute mal mit alten Querys an.

Zunächst versuchen wir eine Stelle zu finden, an der wir ansetzen. Bei den meisten Legacy Codes wurde mit solch einem oder einem ähnlichen Konstrukt die Daten für „Zeige neue Blogbeiträge mit Namen des Autors“ abgerufen, wobei „autor“ und „blog“ zwei verschiedene Tabellen sind.

$_res = mysql_query("SELECT * FROM blog ORDER BY datum DESC");
while ($row = mysql_fetch_array($_res)) {

  $_resAutor = mysql_query("SELECT * FROM autor WHERE autorid = ".$row['autorid']);
  $autor = mysql_fetch_array($_resAutor);
  // Stelle Blogbeitrag mit Autor dar
}

Das ist nicht nur in der Hinsicht des Datenabrufes schlechter Code und vor lauter „code-smells“ könnte einem glatt schlecht werden. Trotzdem gibt es sowas „da draussen“ und dummerweise funktioniert das leider immer noch.

Was kann man verbessern? Zum einen fällt auf, dass es zwei Querys sind. Das muss nicht nur nicht sein, dass ist auch noch ganz schlecht, denn jede Verbindung zur Datenbank braucht Zeit und die sollten wir uns sparen. Argumente wie „mysqli / PDO benutzen“ lasse ich ganz bewusst aussen vor, es soll um Prinzip gehen (sicher wäre die Verwendung eines PDO oder ORM wie Doctrine besser, ganz klar).

Sparen wir uns also den zusätzlichen Query:

$_res = mysql_query("SELECT b.*, a.* FROM blog b, autor a WHERE b.autorid = a.autorid ORDER BY b.datum DESC");
while ($row = mysql_fetch_array($_res)) {
  // Stelle Blogbeitrag mit Autor dar
}

Schon viel besser, aber noch nicht gut genug. Was passiert, wenn ein Blogbeitrag existiert, aber der Autor nicht? Bei so einer Legacy-Anwendung fast schon der Normalfall. Der ganze Beitrag fehlt. Doof, also brauchen wir eine Mechanik, die trotz fehlendem Autor den Blog Beitrag noch anzeigt.
Ganz kurz: Das machen JOINs – und ehe ich nun von vielen gesteinigt werde: JOINs machen noch viel mehr, aber das würde hier den Rahmen sprengen und außerdem möchte ich dazu noch mehr schreiben.

Das ganze nun mit einem LEFT JOIN:

$_res = mysql_query("SELECT b.*, a.* FROM blog b LEFT JOIN autor a ON (b.autorid = a.autorid) ORDER BY b.datum DESC");
while ($row = mysql_fetch_array($_res)) {
  // Stelle Blogbeitrag mit Autor dar
}

Es ändert sich nicht viel, aber nun erscheinen auch alle Blogbeiträge ohne Autor und als kleines Extra arbeitet der letzte Query auch noch etwas schneller als der zweite; super für Legacy-Code, der schon ähnlich wie in Beispiel Zwei aufgebaut ist, denn dort muss man nicht – oder nur wenig – an den PHP Code ran und kann sich auf die reine SQL-Optimierung konzentrieren.

Diese Art der Optimierung alter Legacy-Anwendungen macht relativ wenig Arbeit und bringt dafür recht viel. Vor allem im Bereich von SQL-Code, wo Beispiel eins mehrfach vorkommt (Grundquery, dannach werden viele Querys gestartet die Detaildaten zum Grundquery abrufen, dannach wird gerechnet und wieder neue Querys abgerufen usw.) und damit die Anwendung an dieser Stelle nur sehr langsam ist, kann ein Umstieg auf JOINs und u.a. Verlagerung von Arbeit von PHP in den Query sehr viel Geschwindigkeit herausholen.

Allerdings – und das sollte ganz klar sein – kann dies keine schlechte Architektur ersetzen. Legacy Code kommt irgendwann an den Punkt, an dem ein Optimieren keinen Sinn mehr macht und man sich lieber auf die Neukonzeption konzentrieren sollte. Als Hilfsmittel, um z.B. langsame Bereiche zu beschleunigen, sollte man die Kentnisse allerdings auffrischen. Vor allem, da bei modernen Methoden (PDO, OML, …) die JOINs eine zentrale Rolle spielen.

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.

Windows Developer System – Die Grundlagen (01)

Ich stelle euch hier vor, wie ihr Stück für Stück eine professionelle PHP-Entwicklungsumgebung unter Windows erstellt und das ganze ohne besonders viel Aufwand. Die meisten Tutorials in dieser Richtung – zumindest die, die ich kenne – gehen immer davon aus, dass ihr Linux benutzt – und wenn doch mal Windows erwähnt wird, dann meist mit dem Seitenhieb, doch mal endlich ein „richtiges“ Betriebssystem zu benutzen.

Ich selbst habe 2 Maschinen, beide mit Windows7, und dieses Tutorial basiert daher natürlich auf dem Betriebssystem Windows. Ihr könnt aber davon ausgehen, dass ihr, wenn ihr unter Windows entwickeln wollt, keine Einschränkungen gegenüber den Linux-Leuten habt, ihr habt nur andere Kommandos – auch wenn die Pinguine euch manchmal was anderes erzählen wollen. Am Ende ist doch eh nur der Code wichtig und der läuft sowohl unter Windows wie unter Linux.

Anfangen möchte ich mit einem Überblick und nötigen Grundlagen, alles weitere kommt dann Stück für Stück. In jedem Teil findet sich ein Inhaltsverzeichnis mit den jeweils anderen Folgen dieser Serie.

Auf jeden Fall wünsch ich euch viel Spaß beim lesen und viel Erfolg beim Umsetzen.

Inhaltsverzeichnis:

Windows Developer System – Die Grundlagen

Zuallererst brauchen wir natürlich ein Windows, welches auch immer. Es sollte im Prinzip auf jedem Windows ab XP funktionieren, testen kann ich – wie oben schon erwähnt – nur auf Win7; Kommentare zu anderen Windows-Varianten wie z.B. Vista, XP oder sogar Win2000 wären nett 😉

Linuxianer oder Mac-User dürfen natürlich gern mitlesen. Bei den Anleitungen zur Installation werde ich aber gnadenlos Windows-lastig sein, dafür könnte sich das Mitlesen bei der Benutzung von PEAR und den damit verbundenen Code-Analyse-Tools aber lohnen. Mein Tipp: Dabeilesen ist alles 😉

Eure Festplatte sollte noch ein paar MB oder besser GB Platz haben, natürlich vor allem für die „große“ Software wie den Webserver und die lokale Datenbank, als auch für eine professionelle IDE … die Puristen unter euch können natürlich auch weiterhin im Notepad2, Notepad++ oder dem Programmers Notepad arbeiten; viele Features einer IDE können einem Entwickler in der täglichen Arbeit allerdings sehr viel lästige Arbeit abnehmen (siehe die 10 Regeln für Entwickler – Punkt 3).

Die Frage an dieser Stelle lautet: „Warum sollte ich das machen?“. Die Antwort ist trivial: Weil es deine Arbeit verbessert und erleichtert, weil es dir viele Möglichkeiten gibt, dich selbst und deine Fähigkeiten zu verbessern. Und bestimmt noch mehrere Dutzend Antworten, die dir aber auch alle im Laufe dieses Tutorials klar werden. Darum!

Von hier aus geht es nun also nahtlos weiter zum nächsten Punkt, dem downloaden und installieren des lokalen Webservers.

PHP-Snippet: stripArrayKey

Aus einem QueryString (z.B. dem $_GET) nicht gewollte Parameter löschen:


function stripArrayKey($sInput, $arrKeysToStrip = array()) {

if (count($arrKeysToStrip)<=0) {
return $sInput;
}

$arrResult = array();
parse_str($sInput, $arrResult);

foreach ($arrKeysToStrip AS $keyName) {
if (array_key_exists($keyName, $arrResult)) {
unset($arrResult[$keyName]);
}
}

return http_build_query($arrResult);
}

Vorher:

param1=value&param2=otherValue&myParam=unwanted&param3=moreValue


return stripArrayKey($sQuerystring, array('myParam','param2'));

Nachher:

param1=value&param3=moreValue

Wahlweise auch gleich viele Parameter auf einen Rutsch.

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 😉