Archiv der Kategorie: PHP

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 …

10 Regeln für Entwickler

Es gibt viele „Regeln“ zum Programmieren, viele „Goldene Regeln“; das hier sind meine.

  1. Glaube nicht, wisse!
    Glauben heißt, nicht zu wissen. Sei kein Gläubiger, sei ein Wissender! Hebe dich von der Schar derer ab, die nur Oberflächliches kennen, jene, die nur auswendig lernen, was in den „xyz for dummies“ Büchern steht. Du bist anders, du bist smart: Du verstehst, was da steht!
     
  2. „Know your Enemy“
    ”Kenne deinen Feind”; lerne Sachen, die du hasst; verdamme nicht einfach Dinge, die neu oder anders sind: Lerne Sie, verstehe, warum diese Dinge so sind, warum ein Autor das auf diese Weise gelöst hat und wieso nicht anders. Verinnerliche neues, sei bereit für die Änderung, wiege alle Optionen ab: Und dann nimm die am besten passende!
     
  3. Notepad? Benutz eine IDE!
    Benutze eine IDE; Lerne die Features “deiner” IDE kennen und nutze diese Gnadenlos aus! Verstehe, dass jeder „seine“ IDE braucht und das keine IDE wirklich besser ist als eine andere: Es kommt nur auf den Entwickler an, der den Code schreibt!
     
  4. Benutze Standards
    Standards wurden von schlauen Menschen entwickelt, damit diese auch mit Code zurecht kommen, den weniger schlaue Hacker schrieben; du benutzt deinen eigenen Standard nicht, weil du ein guter Entwickler bist; Du benutzt deinen eigenen Stil, weil du die Standards bisher nicht verstanden hast! (siehe 1,2)
     
  5. Object to go
    Klassen erleichtern dir das Leben: Sie lösen eine Aufgabe und das machen Sie effizient; ist die Klasse gut, macht Sie das sogar auf einer abstrakten Ebene und du kannst die Klasse nicht nur für dieses Problem einsetzen, sondern für alle ähnlichen Probleme. Aus (1) erkennst du, wie toll das ist, du nimmst die Klasse mit und bist fast am Ziel. Klassen und Objekte sind dein Freund: Benutze Sie auch!
      
  6. Kommentiere weise
    Kommentiere deine Funktionen, deine Klassen, eine extrem schwer zu verstehende Stelle im Code oder eine Stelle, in der du mal von der internen Norm abweichen musstest; Kommentiere nichts triviales; Kommentiere keine Kommentare -> das ist Code, kein Chat!
     
  7. Hacke nicht, komponiere
    Programmier kann jeder, wahrhaft entwickeln ist eine Kunst. Die richtige Wahl der Werkzeuge, das Arrangement der Funktionen, die Sitzordnung des Frameworks, die kurze Ruhe vor dem Beginn des Deployments … Du programmierst nicht, du komponierst Code und diese Kunst kann keiner Würdigen … außer ein anderer Codeponist.
     
  8. Bleib auf dem Teppich
    Ja, du bist der tollste Coder der Welt, aber keiner nimmt es zur Kenntnis? Liegt daran, dass keiner deinen Code lesen kann. Lesbarkeit ist wichtig, vor allem für dich selbst. Lies dich mal nach 12 Monaten und 4 Zwischenprojekten wieder in deinen „hochoptimierten“, „perfekten“ und „commentless“ Code ein … und dann weißt du, dass du (3),(4),(5) und (6) nicht verstanden hast. Sechs, setzen … Anfänger!
     
  9. Teile und herrsche
    Wussten schon die Römer, nur du weisst es wieder nicht. Teile dein Wissen, denn wenn du nicht fähig bist zu lehren, dann hast du es nicht richtig verstanden (siehe 1 und 2). Bringe dein Team auf deinen Wissenstand und messe dich regelmäßig mit stärkeren, mit schlaueren, mit „Wissenden“ … denn nur im direkten „Code-Wettkampf“, sei es nun mündlich oder als Code-Kata, kannst du erst über dich hinaus wachsen, deine Schwächen erkennen (und die hast du, glaube mir) und damit erst bist du in der Lage, dich zu verbessern. Wisse, dass du nichts weist; Plato wusste das auch … und du lernst das auch noch vom Kollegen nebenan!
     
  10. Don’t Panic
    Die wichtigste Regel und ich hoffe, ich muss dazu nichts mehr sagen!
Dies sind meine Regeln,
es gibt viele Regeln, aber diese sind meine…
Wie sehen deine aus???

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

PHP_INT_MAX, kein PHP_INT_MIN und ein WTF

Bei der Prüfung von Usereingaben sollte man Zahlen auch dahingehend vergleichen, ob diese auch noch überhaupt im erlaubten Bereich liegen.

Nach oben kein Problem

Ob eine Zahl zu gross wird kann man relativ leicht mit PHP-Bordmitteln feststellen.


if (PHP_INT_MAX<=$meineZahl) {
//...
}>

Damit läßt sich schnell und einfach feststellen, ob eine Zahl den erlaubten Integer-Höchstwert von 2147483647 übertrifft oder eben nicht.

Interessant ist übrigens, dass Zahlen, die über PHP_INT_MAX hinausgehen zu einem Float werden – somit könnte man auch entsprechend den Datentyp vergleichen und darüber dann eine Schlussfolgerung ziehen. Man kann weiterhin ganz normale Größenvergleiche durchführen, allerdings sollte man bei der Nutzung von Filterfunktionen aufpassen:


$ergebnis = filter_var($variable,FILTER_SANITIZE_NUMBER_INT);

Das Beispiel liefert dann keinen Integer mehr, sobald $variable größer PHP_INT_MAX wird und somit könnten entsprechende Vergleiche fehlschlagen.

Eine entsprechende Max-Funktion für Float – PHP_FLOAT_MAX – gibt es leider nicht.

Ein Blick nach unten

Nun könnte man meinen, es gäbe auch ein PHP_INT_MIN, doch #meep#, leider nicht. Warum auch immer: Es gibt keine entsprechende Min-Funktion für Integer!

Kurios wird auch das verhalten des Typs, wenn man sich der Untergrenze des Int-Bereiches nähert. Während man nach oben recht einfach prüfen kann, ob man sich im normalen Bereich aufhält – ob nun durch PHP_INT_MAX oder eine Typprüfung auf int bzw. float – ist das im negativen Zahlenbereich so eine Sache, aber erstmal etwas Code:


$minzahl = -2147483647;
var_dump($minzahl);
var_dump($minzahl-1);

$minzahl = -2147483648;
var_dump($minzahl);
var_dump($minzahl-1);

Das Ergebnis?


int(-2147483647)
int(-2147483648)

float(-2147483648)
float(-2147483649)

Wie man sieht, ist sich PHP nicht so ganz einig, welchen Datentyp denn nun die Zahl –2147483648 hat, Integer oder Float – vielmehr kann in diesem Grenzbereich auch keine Aussage treffen, ob man nun den Bereich unterschreitet oder nicht – zumindest nicht ohne Zuhilfenahme einer Konstanten.

sprintf und float-Zahlen in mySQL-Querys

Floats, also Fließkommazahlen, in eine DB zu schreiben sollte man können. Dachte ich bis vor kurzem 😉

Und deshalb, vor allem als Hinweis an mich selbst: Falls ein Query mit einem – vorher bereits vom Punkt befreiter, das Komma in Punkt umgewandelten – String plötzlich nicht mehr korrekt ausgeführt wird, dann liegt das höchstwahrscheinlich an zwei Sachen; allein, eine war mir dann neu:

  1. Du übergibst deinem Query die Parameter via sprintf (oder äquivalenten davon) und (jetzt kommt das neue)
  2. dein Server spricht plötzlich Deutsch!

Um einen fehlerhaften Query aber dauerhaft zu vermeiden, setze ich vor meinen Query nun die Sprache des Systems auf die Sprache der Datenbank und dannach wieder zurück. Den Quellcode habe ich stark vereinfacht mal hier angefügt.


// Wechsel zur internen Sprache der DB, als Beispiel fest im Code
$old = setlocale(LC_NUMERIC, NULL);
setlocale(LC_NUMERIC, 'us_US');

// Query ausführen. Werte im Query werden via sprintf eingegeben
// ...

// Zurück zur normalen Einstellung
setlocale(LC_NUMERIC, $old);

Ich hoffe, ich kann damit mal jemanden aus der Patsche helfen, wahrscheinlich bin ich aber wieder mal selbst mein bester Kunde 😉

String mit array-strings durchsuchen – Performance der Funktionen

Ich suche in einem String nach Teilstrings, die ich in einem Array habe. Die Funktion array_search macht leider genau das Gegenteil, deshalb brauche ich an dieser Stelle eine eigene Funktion.

Doch welche String-Suchfunktion verwende ich?
Zur Auswahl stehen folgende Funktionen, sollte ich was vergessen haben bitte ich um kurze Meldung:

Hier mal der Versuch einer Messung, wie schnell die Funktionen ihre Arbeit erledigen. Dazu lasse ich alle Funktionen nacheinander den String durchsuchen, das ganze mache ich je Funktion 9999 mal, um eine Tendenz sichtbar zu machen. Und ja: Quick and Dirty Code, ich bin mir dessen bewusst.


$arrHaystack = array( 'hallo',
'welt',
'foobar',
'php5',
'suchfunktion',
'bob',
'und',
'alice',
'suchen',
'strings',
'in',
'arrays',
'Lorem','ipsum','dolor','sit','amet','consetetur','sadipscing','elitr','sed','diam','nonumy','eirmod','tempor','invidunt','ut','labore','et','dolore','magna','aliquyam','erat','sed','diam','voluptua','At','vero','eos','et','accusam','et','justo','duo','dolores','et','ea','rebum','Stet','clita','kasd','gubergren','no','sea','takimata','sanctus','est','Lorem','ipsum','dolor','sit','amet','Lorem','ipsum','dolor','sit','amet','consetetur','sadipscing','elitr','sed','diam','nonumy','eirmod','tempor','invidunt','ut','labore','et','dolore','magna','aliquyam','erat','sed','diam','voluptua','At','vero','eos','et','accusam','et','justo','duo','dolores','et','ea','rebum','Stet','clita','kasd','gubergren','no','sea','takimata','sanctus','est','Lorem','ipsum','dolor','sit','amet','Lorem','ipsum','dolor','sit','amet','consetetur','sadipscing','elitr','sed','diam','nonumy','eirmod','tempor','invidunt','ut','labore','et','dolore','magna','aliquyam','erat','sed','diam','voluptua','At','vero','eos','et','accusam','et','justo','duo','dolores','et','ea','rebum','Stet','clita','kasd','gubergren','no','sea','takimata','sanctus','est','Lorem','ipsum','dolor','sit','amet','Duis','autem','vel','eum','iriure','dolor','in','hendrerit','in','vulputate','velit','esse','molestie','consequat','vel','illum','dolore','eu','feugiat','nulla','facilisis','at','vero','eros','et','accumsan','et','iusto','odio','dignissim','qui','blandit','praesent','luptatum','zzril','delenit','augue','duis','dolore','te','feugait','nulla','facilisi','Lorem','ipsum','dolor','sit','amet','consectetuer','adipiscing','elit','sed','diam','nonummy','nibh','euismod','tincidunt','ut','laoreet','dolore','magna','aliquam','erat','volutpat');

$iIterations = 100;
$suchsatz = array('meas magna','mia diam','im garten sind bob und alice','elixier des nichts');

$iMax = count($suchsatz);
$iStart = microtime(1);

for ($i=0;$i<$iIterations;$i++)
{
$iTake = mt_rand(0, $iMax);
foreach ($arrHaystack AS $key => $value)
{
if (mb_substr_count($suchsatz[$iTake], $value)>0)
{
break;
}
}
shuffle($arrHaystack);
}
$iEnd1 = microtime(1);

for ($i=0;$i<$iIterations;$i++)
{
$iTake = mt_rand(0, $iMax);
foreach ($arrHaystack AS $key => $value)
{
if (mb_stripos($suchsatz[$iTake], $value)!==FALSE)
{
break;
}
}
shuffle($arrHaystack);
}
$iEnd2 = microtime(1);

for ($i=0;$i<$iIterations;$i++)
{
$iTake = mt_rand(0, $iMax);
foreach ($arrHaystack AS $key => $value)
{
if (mb_stristr($suchsatz[$iTake], $value)!==FALSE)
{
break;
}
}
shuffle($arrHaystack);
}
$iEnd3 = microtime(1);

for ($i=0;$i<$iIterations;$i++)
{
$iTake = mt_rand(0, $iMax);
foreach ($arrHaystack AS $key => $value)
{
if (preg_match('/'.$value.'/i', $suchsatz[$iTake])>0)
{
break;
}
}
shuffle($arrHaystack);
}
$iEnd4 = microtime(1);

printf('--------------------------'."\n");
printf('mb_substr_count() -> %f'."\n",$iEnd1-$iStart);
printf('mb_stripos() -> %f'."\n",$iEnd2-$iEnd1);
printf('mb_stristr() -> %f'."\n",$iEnd3-$iEnd2);
printf('preg_match() -> %f'."\n",$iEnd4-$iEnd3);
printf('--------------------------'."\n");

Hier die Ergebnisse:
————————–
mb_substr_count() -> 15.756097
mb_stripos -> 16.490042
mb_stristr -> 16.938911
preg_match -> 14.681287
————————–

Schön zu sehen, dass meine intuitiv gewählte Funktion mb_substr_count() die Arbeit anscheinend am schnellsten erledigt. Überraschend für mich, dass der regex Platz 2 belegt und dass die anderen Funktionen einen so großen Abstand zu den beiden ersten Plätzen aufweisen.

Update 08.01.2011:
Habe den Code mal den Kommentaren angepasst. Das Array der Wörter ist nun 240 Felder groß, es wird nun auch zufällig einer von 4 Sätzen durchsucht. Nach jedem Treffer wird das Wort-Array zufällig neu ‚geschüttelt‘. Das sollte alle Caching-Sachen ausschalten.

Dabei bildet sich wirklich ein neues Bild (bei 100 durchläufen). Platz 1 geht nun an den regex, Platz 2 an substr_count, dicht gefolgt von den beiden stri-Funktionen. Insgesamt ist aber kein nennenswerter Abstand mehr vorhanden (im Gegensatz zum ersten Test), weshalb ich sagen kann: Nehmt, womit ihr am besten klarkommt. In High-Performance Umgebungen folgt dem Testergebnis.

KISS – oder wie umständlich muss es noch werden?

Passend zum DRY-Artikel nun der zum Thema KISS. Nein, nicht die Rock-Band-KISS, sondern ein Prinzip der Softwareentwicklung, das besagt: „Mache es so einfach wie möglich!“.

Immer wieder sehe ich Code, der akademisch bestimmt mit Note 1 ausgezeichnet wird. In Java, C++, Delphi oder anderen compilierenden Sprachen hat das ganze auch durch seinen Sinn und seine Daseinsberechtigung. Aber in PHP sieht die Sache anders aus.

PHP ist eine Interpretersprache und wird es auf absehbare Zeit auch erstmal bleiben. Interpretersprachen haben im Grundprinzip eines gemeinsam: Sie durchlaufen den Code während der Ausführung Zeile für Zeile, Schritt für Schritt. Und genau da liegt der Schwachpunkt: Je mehr Zeilen ein Code hat, desto länger braucht der Interpreter, den Code zu durchlaufen. Je länger der Interpreter braucht, umso größer wird die „Time-to-Response„, also diejenige Zeit, die zwischen Anforderung an den Server (Aufruf der Seite) und Antwort des Servers (Auslieferung der Seite, also des generierten Quelltextes) liegt. Und diese Zeit sollte „as small as possible“ sein (wer wartet schon gern auf seine Seite).

Dieses Problem potenziert sich meist, da der normale User erstmal eine (allgemeine) Startseite ansurft und sich dann durch die Seite bewegt; dabei ruft er meist spezialisierte Seiten mit mehr Aufgaben für den Server auf. Mehr Aufgaben, mehr Inhalt, mehr Code, länger warten, nicht gut!

Warum nun das ganze hier im Zusammenhang mit PHP?
In PHP lassen sich selbstverständlich schöne Objektorientierte Konstrukte bilden, die auch von vielen Seiten als „richtig“ proklamiert werden. Ein Beipiel sei dies (in Pseudocode, nicht wundern):



holeBenutzerdaten();
// mach was damit


// ab hier nun die funktionen, diese liegen alle
// in unterschiedlichen klassen, dateien usw.

function holeBenutzerdaten()
{
prüfeEingabe();
ladeBenutzerdaten();
return;
}

function prüfeEingabe()
{
prüfeSpezifischeEingabe;
ladeIrgendwasUnwichtiges;
prüfeNochmal;
}

function prüfeSpezifischeEingabe()
{
prüfeObEingabeValideIst
ja: prüfeDieEingabeNochmalMitWasAnderem
nein: ladeFehlerbehandlung; gibFehlerAnFehlersystem; zeigeFehler;
}

// So, und nun noch 20 Dateien mehr und ca. 60 Funktionsaufrufe

Nicht schön, denn hier wird, anstatt das Problem zu erledigen, von einer Funktion zur nächsten gesprungen. Unschön dabei: Jede Zeile kostet Zeit und die gilt es in einem guten Projekt so gering wie nur möglich zu halten.

Es gilt das Prinzip: So viel wie nötig, so wenig wie möglich! Nicht mehr, nicht weniger. Dazu aber noch später mehr.

Und nun Beispiel 2: Die reine Lehre sagt, dass veränderliche Werte in Variablen abgelegt werden sollen und diese Variablen dann an einen einheitlichen Ort. Nennen wir das mal „Config-Dateien“, dann kann sich jeder was drunter vorstellen. Diese Config-Files kennen wir alle, meist stehen da mindestens die MySQL Zugangsdaten drin, manchmal auch noch was anderes, zumeist belangloses. Das ist soweit auch gut und richtig und sollte – in einem gewissen Rahmen – auch gemacht werden … allerdings sollte man es dabei nicht übertreiben.

In diesem Beispiel möchte ich zeigen, wie man – unter dem Aspekt des Konfigurierens und Auslagerns – eine ganz simple Sache sehr kompliziert machen kann … und dafür in gewissen Kreisen sicherlich noch großen Beifall ernten wird.

Urspung des ganzen ist ein Aufruf, ähnlich wie dieser:


mysql_query("SELECT * FROM user");

Warum schreibt nun in die Config-Dateien nicht rein, wie die Tabellen heißen und benutzt dann nicht den statischen Wert, sondern die Variable? Und warum nehmen wir dann nicht eine Funktion? Die kann man dann immerhin noch mit Unit Tests auf ihre korrekte Funktion testen!? Das sieht dann so aus:


public function getTbluser() { return 'meineUserTabelleInMysql'; }

und die kann man dann so nutzen:


mysql_query("SELECT * FROM ".getTbluser());

Toll, oder? Ändert sich nun der Tabellenname, dann muss man das nur an einer Stelle ändern und gut ist. Kommt doch dem DRY-Prinzip zugute.
Noch besser wäre es doch, wenn wir nun auch alle Feldnamen in festlegen … weil … die können sich ja auch ändern. Also los, Feldnamen auch. *tipptipptipp* Toll. Ach ja, die Variablennamen könnten sich ja mal ändern. Also auch die rein und per $$ eingebungen, damit es auch geht.

Und der Code sieht dann doch gleich viel … übersichtlicher aus:


mysql_query("SELECT ".getTblUserField1().",".getTblUserField2()." FROM ".getTblUser()." WHERE ".getTblUserFieldSort()." = $$configFileTblUserFeldSort");

Super, oder? Kommt man nun noch dazu, diese ganzen, sich ständig verändernden SQL Kommandos noch zu ersetzen, dann hat man doch ein völlig einfach konfigurierbares System vor sich, oder etwa nicht?

Ihr merkt es schon, ich drifte ins Zynische ab – aber nur gaaanz leicht, das mag daran liegen, dass ich von diesem Code in letzter Zeit zu viel gesehen habe – , nochmal also zur Klarstellung:
Nein, so macht man es nicht!

Warum nicht? Dazu gibt es gleich mehrere Punkte.
Zum einen kann man es nicht mehr lesen. Das ist ein sehr wichtiger Punkt, unterschätzt das nicht, denn zum einen sitzt ihr nicht bis in alle Ewigkeit an einem Code – nichts ist schlimmer, als nach Monaten wieder alten Code lesen zu müssen und nicht zu verstehen – und zum anderen werdet ihr an der Qualität eures Codes auch innerhalb der Entwicklungsabteilung gemessen! Schwer verständlichen Code zu schreiben mag einen Marketing-Menschen noch leicht beeindrucken können, ein guter Programmierer dagegen wird euch – wenn ihr Glück habt nur leise – verfluchen.

Zum anderen muss der Interpreter jedesmal wieder in die entsprechende Funktion springen und das braucht Zeit. Nein, viel ist das nicht, wenn das nur eine Tabelle ist, nur ganz, ganz wenig Zeit, wirklich. Allerdings: Hat man erst so eine schöne Funktion, die einem die entsprechenden Tabellen und Felder referenziert, dann benutzt man das doch nicht nur für die Abfrage selbst, nein, auch im Code kommen dann anstelle der Feldnamen die Funktionsnamen vor.


$res = mysql_query("SELECT ".getTblUserField1().",".getTblUserField2()." FROM ".getTblUser()." WHERE ".getTblUserFieldSort()." = $$configFileTblUserFeldSort");
$name = $res[getTblUserField1()];
// anstelle von
// $name = $res['name'];

(ja, ich weiß, dass da ein mysql_fetch_irgendwas fehlt)

Und das für jede Tabellenabfrage im Code … und jedes Feld … in jeder Abfrage … und dazu noch in jedem Kontrollkonstukt im Code, also jeder for-Schleife, jeder Ausgabe, in jedem if, in jedem switch und und und … Leute, das summiert sich. Da kommen schnell dutzende Funktionssprünge vor und das sind Zeiten, die nicht sein müssen!

Warum sollte meine Anwendung langsamer sein, als es sein müsste. Die wird von ganz allein langsamer, sobald nämlich immer mehr Benutzer gleichzeitig was wollen, dann summieren sich die Zeiten nicht nur, nein, die multiplizieren sich mit jedem Request! Also, jeder Flaschenhals ist es wert, dass er refaktorisiert wird. Warum nicht gleich von Anfang an alles richtig machen?

Und mal ehrlich: Wie oft ändern sich die Tabellennamen und man ihr als Entwickler müsst wirklich nur die Configs ändern? Na, ehrlich, komm *inDieSeiteStubs* Na also, noch nie gesehen, jede Umstellung der Tabellennamen hatte schon immer eine mehrstündige Umstellungsphase begründet … zusammen mit dem Verlust einiger wichtiger Gramme Körpergewicht 😉

Und hatten wir nicht eben erst erwähnt, wie wichtig die Lesbarkeit des Codes ist? Sicher, ihr habt das Projekt grade erst begonnen und wisst noch alles aus dem Kopf. Aber, wie gesagt, ihr seit meist nicht allein an einem Projekt und – selbst wenn – auch nicht immer „am Stück“ dabei. Euer Kollege würde gern wissen, warum ein bestimmter Request langsam ist oder gar nicht funktioniert und schaut im Code noch und trifft auf so einen Query. Nun muss erstmal mühsam der eigentliche, der „echte“ Query zusammengebaut werden, denn der Kollege hat keine Ahnung, welche Felder ihr ansprecht.

Aber der Tabellenname könnte sich doch mal ändern!
Warum sollte sich der Tabellenname ändern? Ja, auf Shared-Hosting-System kommt es schon mal vor, dass man in einen Webspace viele Webanwendungen packt und da kann es auch vorkommen, dass mehrere Anwendungen ihre Benutzertabelle eben ‚user‘ nennen. Warum auch nicht, es ist der passendste Name dafür! In solchen Fällen – wenn ich also weiß, dass so eine Situation eintreten kann – benutze ich die Option eines Praefix, der jedem Tabellennamen vorgestellt wird und an zentraler Stelle definiert ist.


// Im Config-File
define('DB_PRAEFIX','myPraefix_');

// Im Code
mysql_query("SELECT * FROM ".DB_PRAEFIX."user");

Das hat den Vorteil, dass man den SQL immer noch lesen kann und auch die Tabelle auf dem DB-Server wiederfindet, auch wenn man keine Kenntnis über den Inhalt von DB_PRAEFIX hat. Der Code bleibt lesbar und so ziemlich jeder Entwickler weiß auch, warum ihr das macht.

DAS ist es zum Beispiel, was dass KISS Prinzip sagt: Macht es einfach! Nicht nur euch, sondern auch anderen. Nein, ihr sollt nun dabei nicht vergessen, Sicherheitsmechanismen einzubauen oder Usereingaben zu filtern, wir wollen auch nicht zurück ins PHP3 Monolithen-Zeitlalter, aber dazu bedarf es keiner Funktion, die eine Funktion aufruft, die eine Funktion aufruft, die wiederum … ihr ahnt es schon.

Meine KISS Anforderungen sind ungefähr diese

  • – Die Gesamttiefe der Funktionsaufrufe darf 3 Ebenen nicht überschreiten (die aufgerufene Seite nicht eingeschlossen). Je weniger, desto besser.
  • – Funktionen sollten so wenig wie nötig aufgerufen werden
  • – Funktionen sollten etwas machen, nicht nur Namen liefern (return ‚tbluser‘; )
  • – Funktionen sollten das machen, wofür sie da sind. Nicht mehr!

„Klar“, sagt nun jeder, „aber wie bekomme ich dies oder das dann hin, das geht dann gar nicht mehr“.
Bei vielen dieser Fragen kann man sagen, es ist die berühmte Ausnahme von der Regel und sicherlich ist hier und da eine Ausnahme sicherlich ratsam. Man soll schließlich das ganze gerede und die ganzen schönen Buzzwords nicht als „In-Stein-gemeisselt“ verstehen. Leider zeigen viele dieser Ausrufe aber auch, wie groß die Abhängigkeit von bestimmten Frameworks oder Programmierparadigma ist.

Ich will hier keine Diskussion auslösen über Sinn und Unsinn von Frameworks oder Paradigmen, ich zeige – wie so oft in der Softwarearchitektur – das Optimum. Der Rest liegt bei euch, der Weg ist das Ziel 😉

Über eure konstruktive Meinung dazu würde ich sehr freuen – obwohl ich mehr mit empörten Kommentaren rechne *schnellUnterDenTischDuck*!

Pattern? Sowas mache ich nicht!

Der PHP Hacker fragt „Haben Pattern versagt?“ und hier ist mein Senf dazu.

Mag jetzt auch mal ein wenig den Stil der „anonymen Programmierer“ haben, aber ich programmiere nicht mit dem Ziel, ein bestimmtes Pattern umzusetzen, sondern mit dem Ziel, ein bestimmtes Problem zu lösen. Dies mache ich auf dem – für mich – besten Wege und der Weg ist für jedes Problem, sogar für jeden Kunden, anders. Meist weiß ich gar nicht, dass ich gerade ein bestimmtes Pattern umsetze oder dass der Weg, wie ich etwas mache, schon einen „XY Pattern“ namen hat.

Schande über mich – ich würde bei dem im Artikel genannten Recruiter wohl keinen Job bekommen – aber meine Fragen wären wirklich:

  1. Muss man Pattern (auswendig) kennen, um gut programmieren zu können?
    Sicher sind Kentnisse über „best practices“ – und aus diesen sind die Pattern ja entstanden – hilfreich, aber muss ich deswegen gleich „in Pattern denken“? Sicher nicht.
  2. Ist es sinnvoll, auf Biegen und Brechen ein Pattern umzusetzen, auch wenn die Problemlösung darunter leidet?
    Ganz klar: Nein! Das Pattern sollte sich immer der Problemlösung unterwerfen! Was nützt es mir, dass mein Code in 2.000 Zeilen das XY Pattern sauberst umgesetzt hat, die Problemlösung aber nur 20 Zeilen lang gewesen wäre (und kein Spagetthi-Code ist!).
  3. Muss ich mich nun schämen weil ich das XY Pattern nicht kenne oder bin ich etwa kein guter Programmierer?
    Ich denke, es kommt drauf an. Wenn man die „best practices“ versucht umzusetzen und sauberen, lesbaren, skalierbaren und funktionsfähigen Code schreibt, dann muss man nicht unbedingt sagen können, dass dies nun teilweise X, Y und Z Pattern darstellt. Es kommt IMHO drauf an, dass der Code die o.a. Kriterien erfüllt und nicht zwanghaft Punkt 2 umsetzt.

Das wäre mein – wie gesagt – mein Senf dazu. Was denkt ihr darüber?