Mein Beitrag zum Developers Shame Day

Mein Beitrag zum Developers Shame Day am 03.11.2010:

Oh, wie ich mich schäme, der Code ist von Oktober 2002, stammt aus einem Teil, der für ein Onlinerollenspiel vorgesehen war (damals gab es das Wort Browserspiel noch gar nicht – trotzdem wollte ich sowas bauen; leider war das Projekt damals viel zu groß für mich).

Im Großen und Ganzen habe ich allerdings grade dadurch, dass ich diesen Code geschrieben habe, davon genervt war, wieviel „Arbeit“ das immer wiederkehrende Tippen von < und > überdrüssig war und hiermit erste, zarte Schrittchen im Bereich PHP und Klassen getätigt habe überhaupt erst näher zu OOP in PHP 3+4 und Verwendung von Template Engines (direkt im Anschluss war es FastTemplate, kurz dannach Smarty … bis heute) … soviel zum Thema „sich weiterentwickeln durch schlechten Code“!

Hab sowas mal bei thedailywtf gesehen, hatte es aber in dem Moment doch schon zu sehr verdrängt, als dass da was geklingelt hätte.


class pageproducer
{
function getDoctypeInfo()
{
return "\n\n";
}

function getStartingTag($tagname)
{
return "<".$tagname.">\n";
}

function getEndingTag($tagname)
{
return "\n";
}

function getMetaTagEquiv($name,$content)
{
return "";
}

function getTitle($name)
{
return "".$name."";
}

function getCSS($cssurl)
{
return "";
}

function getHeader()
{
$header = $this->getMetaTagName( "keywords", "") . "\n";
$header .= $this->getMetaTagName( "description", "") . "\n";
$header .= $this->getMetaTagName( "robots", "") . "\n";
$header .= $this->getMetaTagName( "revisit-after", "") . "\n";
$header .= $this->getMetaTagEquiv( "Content-Type", "text/html; charset=iso-8859-1") . "\n";
$header .= $this->getMetaTagEquiv( "expires", "0") . "\n";
$header .= $this->getTitle(HEAD_NAME) . "\n";
$header .= $this->getCSS("rpg.css") . "\n";
return $header;
}

function getHeadSection()
{
$head = $this->getStartingTag("head");
$head .= $this->getHeader();
$head .= $this->getEndingTag("head");
return $head;
}


}

Ich persönlich finde das Konstrukt wirklich schlimm und oberpeinlich; ich komme also heute mit Papiertüte über dem Kopf zur Arbeit 😉 Aber was soll’s, mittlerweile sieht mein Code nicht mehr so aus; mittlerweile übergebe ich das Charset als Parameter #muhaha 😉

Mal ernsthaft: Ich finde die Idee des Ganzen „Developers Shame Day“ sehr gut. Gut für Newbies, dass auch wir „Pros“ mal „Newbies“ und „Script-Kiddies“ waren; gut für uns „Pros“, um unsere eigenen Wurzeln zu reflektieren und nicht zu vergessen.

DRY – redundante Funktionen in PHP vermeiden

DRY – Don’t repeat yourself.
Das kennt jeder, das _sollte_ jeder wissen. Und so gut wie jeder, der etwas Ahnung hat, versucht es so oft es geht umzusetzen.

Allerdings: Das gilt nicht nur für Funktionsnamen, das DRY-Prinzip geht weiter. Es meint IMHO auch, dass man für viele ähnliche Abfragen sich am besten Wrapper baut, die dann die Anfrage an eine Funktion leiten, die die eigentliche Antwort produziert. Die Wrapper „füttern“ die eigentliche Funktion nur mit den richtigen Fragen.

Ein Beispiel: Siggi Scriptkid hat ein Klasse zur Benutzerverwaltung geschrieben, die braucht er in seinem aktuellen Projekt. Er speichert seine Benutzerdaten in einer MySQL Datenbank und muss nun seiner Klasse folgende Fragen stellen und erwartet folgende Antworten:

  • Hole mir den Benutzer mit der ID 5
  • Hole mir den Benutzer mit der Mailadresse xyz
  • (noch viele weitere, ähnliche Fragen mehr)

Siggi schreibt also ganz fleißig Funktionen in seine Klasse:
(Stark vereinfacht, es fehlen Filtern der Usereingaben, maskieren der SQL String und vieles mehr; der Code dient nur als Beispiel!)


class User
{
public function getUserByID($userid)
{
$ressource = mysql_query('SELECT vorname, name
FROM user
WHERE userid = '.$userid);
$user = mysql_fetch_assoc($ressource);
return $user;
}

public function getUserByMailadress($email)
{
$ressource = mysql_query('SELECT ID, mail, userlevel
FROM user
WHERE mail = "'.$email.'"');
$user = mysql_fetch_assoc($ressource);
return $user;
}
}

Nun trifft man auf folgendes Problem: Die Felder der jeweiligen Rückgabe sind unterschiedlich. Auch wenn Siggi nun hingeht, und in beiden Funktionen die Felder gleich setzt, ergeben sich Wartungsschwierigkeiten:

  • Was ist, wenn noch mehr, ähnliche Funktionen hinzukommen? Dann bekommt Siggi wirklich Arbeit!
  • Was ist, wenn Siggi der „user“ Tabelle noch mehr Felder hinzufügen muss und es 6 Funktionen gibt?
  • Was ist, wenn Siggi auch nur eine der Funktionen vergisst?

Um es kurz zu machen: Hier greift das DRY-Prinzip NICHT! Der o.a. Code sollte einem Entwickler, der nicht Siggi Scriptkid heißen möchte, nicht passieren.

Aber wie wende ich das DRY-Prinzip nun an?
Okay, ich schreibe mal meinen Vorschlag dazu:


class User
{
public function getUserByID($userid)
{
return $this->getUser('ID='.$userid);
}

public function getUserByMailadress($email)
{
return $this->getUser('mail='.$email);
}

private function getUser($where)
{
$ressource = mysql_query('SELECT ID, vorname, name, mail, userlevel
FROM user
WHERE '.$where.'
LIMIT 1);
$user = mysql_fetch_assoc($ressource);
return $user;
}
}

Ich schaffe eine private Methode, die die Daten aus der Datenbank holt. Diese ist die einzige Methode, die das macht, somit habe ich nur eine Stelle, in der ich zukünftig Felder ändern muss, wenn sich welche ändern.

Die „alten“ Funktionen verbleiben als Wrapper-Funktionen und übergeben der privaten Funktion nur die richtigen Parameter, um ihre eigene Aufgabe erfüllen zu können. Dann leiten Sie Antwort einfach als eigene wieder zurück.

Auf diesem Wege verinfache ich die Pflege, bin in der Lage, meine alten Funktionen weiterhin nutzen zu können – mit dem Vorteil, dass nun alle Antworten einheitlich sind – und bin darüberhinaus in der Lage, sehr schnell verschiedene neue Funktionen zu schreiben, die mit neuen Fragestellungen klarkommen, z.B. Benutzer mit E-Mail x und Passwort y.

Was haltet ihr von dieser Vorgehensweise. Ich selbst halte sie für praktiabel und gut, was meint ihr? Gut? Schlecht? Gibt es bessere Vorgehensweisen?

Arrayelemente zählen mit Smarty

Die Anzahl der Elemente eines Arrays in Smarty erhält man mit


{$myArray|@count}

oder mit


{@count($myArray)}

Damit lassen sich schöne Ausgaben zaubern, in dem man etwa prüft, ob ein Array Werte beinhaltet (z.B. nach einer DB-Abfrage) oder eben nicht und dann, je nachdem, die Ausgabe Benutzerfreundlich macht:


{if @count($myArray)>0}
Toll, es gibt {@count($myArray)} Einträge :)
{else}
Sorry, keine Einträge gefunden :(
{/if}

Die Angabe des @-Modifikators gibt an, dass nun eine PHP Funktion folgt. Intern wird also die PHP-Funktion count aufgerufen und dessen Rückgabe dann eingesetzt und benutzt.

Aber aufpassen: Wenn die $security Variable auf true steht, dann muss jede PHP-Funktion, die man so benutzen möchte, auch im $security_settings[‚MODIFIER_FUNCS‘]-Array stehen, sonst funktioniert der Aufruf nicht!

Quelle: Smarty-Doku, Kapitel 5 – Variablen Modifikatoren

Alles loggen, alles!

Ich stieß kürzlich auf diesen Artikel:
Log Everything All the Time
und fand diesen sehr interessant.

Es geht um’s mitloggen von Informationen und der damit verbundenen verbesserung der Debugfähigkeit von Software für dessen Entwickler. Das ganze klingt für mich sehr interessant, vor allem der Punkt, dass man (theorethisch) jeden Zeitpunkt zu jeder Zeit nachstellen kann – klingt sehr nach ZendServer 😉 – wäre für mich zumindest manchmal sehr wünschenswert.

Allerdings kann ich mich gut vorstellen, da so ein logging sehr an der Performance kratzt, aber der Autor gibt weiter unten im Artikel sehr nützliche Tipps, wie man das maximum herausholen kann, wenn man nur schon beim Design ein paar – wie ich finde fundemantal wichtige – Regeln beachtet, z.B. „Formatiere nichts, bis es nicht gebraucht wird“ oder „Nur ein Tabellenlookup pro Log“.

Von mir aus kommt da noch eins hinzu: „Benutze keine Frameworks!“. Au weia, jetzt hauen mich alle Zend-Freaks. Ich will damit sagen, dass beim Thema ‚logging‘ auch die Zeit eine sehr kritische Rolle spielt – wir reden hier ja nicht über ein oder zweimal loggen pro Aufruf, sondern jeden Request, in jeder Funktion, in jeder Klasse, quasi in jeder Zeile – und da zählt jede Nanosekunde. Frameworks haben diesbezüglich aber einen Nachteil: Sie haben Overhead.

Wie auch immer, ich würde gern eine Klasse für dieses Logging entwickeln und würde mich freuen, von euch Anregungen zu bekommen, was diese Klasse machen soll, wie Sie aufgerufen werden sollte (instanz, static, …) und wie und wo die Daten gespeichert werden sollten (spontan kommt mir da mysql in den Sinn, wegen durchsuchen usw.). Ich denke, wenn wir alle Ideen sammeln, können wir eine gute Logging Klasse entwickeln, die alle diese Informationen in einem guten Zeitrahmen erfasst und speichert. Das auslesen kann ja von mir aus wieder längern dauern, da kommt es nicht so auf die Zeit an.

*hm* Eventuell gibt es ja schon so eine Klasse, irgendwo, allein oder versteckt in einem Framerwork. In dem Fall bitte ich um einen kurzen Hinweis, dann kann ich mir die Arbeit sparen und direkt produktiv loggen.

Was denkt ihr über den Artikel? Was haltet ihr von so einer Klasse?

P.S.: Sorry, die Klasse soll primär für PHP sein, die Ideen natürlich für alle!

CouchDB Gratis Buch und 100ster Post

Wer sich für Datenbanken interessiert, der landet derzeit u.a. bei CouchDB. O’Reilly stellt passend dazu ein kostenloses Buch ins Netz, wahlweise Englisch oder auch Deutsch, mit dessen Hilfe man tiefer in die Materie vordringen kann.

„CouchDB – The Definitive Guide“ bei guide.couchdb.org

Vielen Dank an Daniel von ebene7 für den Tip!

P.S. Das war er nun, der 100ste Blogeintrag!

Brother DCP-Drucker: „Fehler: Reinigen“

Nur, damit das ganze nicht verloren geht: Ich bekomme oft auf meinem Brother-DCP-Drucker die Fehlermeldung „Fehler: Reinigen“ bzw. es werden leere Partonen anmoniert, die noch ganz voll sind.

Zur Behebung des ganzen muss man in das Servicemenü des Druckers gehen und diesen Resetten. Wie das geht, zeigt Uwe in seinem Blog. Und damit diese Information nicht verloren geht, verbreite ich sein Wort mal.

Hier der essentielle Auszug aus seinem Beitrag, den Link zum ganzen Beitrag gibt’s am Ende:

„[…] Menü, Start s/w/, 4x Pfeil nach oben. Wir befinden uns im Maintenance-Mode. Aber keine Zeit für Verschnaufpausen, das muss schnell gehen, sonst werdet ihr Piepgeräusche zu hören bekommen, die euch um Gnade flehen lassen. So lange Pfeil nach oben drücken, bis eine 8 erscheint. Mit Eingabe bestätigen. So lange Pfeil nach oben drücken, bis eine 0 erscheint und wieder mit Eingabe bestätigen. Jetzt solange die Start s/w Taste drücken, bis ihr das Wörtchen Purge + irgendeine Zahl lesen könnt. Purge bedeutet übersetzt soviel wie Säuberungsaktion und wird wohl für den Zähler stehen. Jetzt wieder mit der Kombination Pfeil nach Oben und Eingabe folgende Zahlenkombination “einhämmern”: 2, 7, 8, 3. Dann den schönen roten STOP Knopf. Und wieder die Kombination Pfeil nach Oben und Eingabe für 9, 9. Das Ding “rebooted” und.. druckt wieder. […]“

Vielen Dank an Uwe, ohne den ich bestimmt schon einen neuen Drucker hätte teuer kaufen müssen.

Quelle: http://uwe.vg/2008/09/02/fehlerreinigen-how-to-reset-your-silent-brother/

P.S. Und grade erst merke ich, dass Uwe und ich ja Städtenachbarn sind 😉

Nachtrag 12.10.2010
Jaja, Asche auf mein Haupt. Das Gerät heißt DCP, nicht MCP. Und das trotz Brille …;)

UTF Dateien mit PHP fopen erzeugen

Will Mensch eine „UTF-8“ Datei mit PHP’s eigener fopen-Funktion erzeugen, muss er nur ein paar Dinge beachten.

Zum einen muss der jeweilige Inhalt des fwrite-Befehl UTF-8 encoded sein; das erreicht man am sichersten über die Funktion mb_convert_encoding.

Dann erzeugt man das File auf jeden Fall mit dem ‚b‘ Parameter, sicher ist sicher.

Als letztes setzt Mensch in der Datei noch die entsprechende „Byte Order Mark“ hinein. Das passiert mit Hilfe des pack-Kommandos.

Letztendlich kann man dann seinen Content in die Datei schreiben.


$content = mb_convert_encoding($content,'UTF-8');
$fHandle = fopen($filename,'wb');
fwrite($fHandle, pack("CCC",0xef,0xbb,0xbf));
fwrite($fHandle, $content);
fclose($fHandle);

P.S.: Nicht das fclose vergessen, das wäre einfach schlechter Stil 😉

„43/-infected: UTIL_SetModel: not precached: error.mdl“

Sorry, wollte dieses Blog eigentlich von allen „Spiele“-Einträgen freihalten, aber aus gegebenem Anlaß … bin ja schließlich auch nur Studier-Papa-Workaholic mit Ambitionen zum zocken.

Und: Ja, ausgerechnet ein „Killerspiel“, #pfui #bäh

Also, ganz kurz: Left4Dead2 „The Sacrifice“ startet nicht, Fehlermeldung:
„43/-infected: UTIL_SetModel: not precached: error.mdl“
Die Lösung: Entfernt alle AddOns aus dem Ordner:
„steam\steamapps\common\left 4 dead 2\left4dead2\addons“

Thanks to http://forums.steampowered.com/forums/showpost.php?p=17366121&postcount=18

Nachtrag vom 11.10.2010
Da das Spiel nach einmal spielen dann den Fehler „Dieses Spiel ist zurzeit nicht verfügbar.“ anzeigte und mit nichts zur Besserung zu bewegen war, musste ich Left4Dead2 komplett löschen und neuinstallieren. Nun funktioniert alles, inklusive Untertitel usw.

Keine UTF-8 Dateien in Windows

Windows erstellt Ordner mittels mkdir in UTF-8 nicht korrekt: Sonderzeichen werden nicht als solche behandelt, sondern als „ecoding-müll“ (är) dargestellt. Die Lösung: Das Encoding des Strings in der Funktion mkdir muss ISO sein.

Beispiel: Aus der DB kommt der Projektname ‚Hallo Bär‘, dieser steht im Array $projectData im Feld ’name‘ und liegt im UTF-8 Encoding vor.


echo mb_detect_encoding($projectData['name']);
$outputdir = $projectData['name'].DIRECTORY_SEPARATOR.date('YmdHis');
echo mb_detect_encoding($outputdir);
mkdir($outputdir,0755,true);
$outputdir2 = mb_convert_encoding($outputdir, 'iso-8859-15');
echo mb_detect_encoding($outputdir2);
mkdir($outputdir2,0755,true);
exit;

Ausgabe ist einmal, dass $outputdir UTF-8 ist, allerdings wird das VZ „Hallo Bär“ als „Hallo Bär“ erstellt, nach der Konvertierung gibt PHP zwar immer noch UTF-8 aus, allerdings wird das VZ nun korrekt erstellt.

Anmerkung: Das ist ein vereinfachter Beispielcode, nicht, dass es hinterher wieder heißt, der Sascha baut keinen schönen Code 😉