Experimentelle Analysen von PHP Code für Enthusiasten

Während ich im vorhergehenden Artikel die „Standardtools“ für die Analyse von PHP verwendet habe, möchte ich hier einen Schritt weitergehen. Wenn ich schon meine Programme analysieren lasse, dann richtig :)! Darum hier ein paar nicht-standard Analysetools für PHP, die ich als eher experimentell bezeichnen würde:

  • doxygen
  • phuml
  • php-lint
  • PHP Callgraph

Weiterlesen

PHP Code-Analyse mit Ant

Graph LeadIch mag Code. Nicht jeden natürlich, „Douchebag“ Code nervt, Code der aussieht wie ein schreiender Quasimodo oder Code der von einem Dr. Frankenstein in Ausbildung erstellt wurde sind bemitleidenswert und sehr viel Code stinkt. Code, der gefällt (wie beispielsweise der von Frigidor) ist clever ohne zu bluffen, kurz aber nicht kryptisch, tut etwas, ist lesbar und hat einige Wows drin.

Diese Bewunderung sollte in Zahlen gefasst werden: mit Statistiken, Diagrammen, Balken und Graphen. Dies dient zur Vorbereitung der „Continuous Integration“ mit Jenkins/Hudson, die in dieser Serie behandelt wird. Als Quellen haben vor allem jenkins-php und ein äusserst Lesbarer Artikel im Entwickler Magazin 01/11 gedient.

Hier wird nun gezeigt, wie mit einem Befehl:

  • Die Unittests durchführt.
  • Qualitative Softwaremetriken mit PHP Depend misst: Hierarchietiefe, Komplexität, …
  • Unschöne Teile mit PHPMD identifiziert.
  • Copy-Paste-Verbrecher aufspührt mit phpcpd.
  • Den Coding-Style prüft mit dem PHP Code Sniffer.
  • Quantitative Softwaremetriken mit phploc misst.
  • Die PHPDoc-Doku erstellt.
  • Die Resultate mit dem PHP Code Browser schön darstellt.

Weiterlesen

Ich weiss (nicht mehr) ob Du im Facebook bist…

Update 27.01.2011: *übel_fluch*! Facebook hat ein Captcha eingebaut, dass diese Detection momentan nicht mehr funktionieren lässt. Das Prinzip bleibt ok, die Realisierung brauchte nun etwas mehr Zeit.

Big BrotherEigentlich hätte ich ja ganz Anderes zu tun, aber per Zufall bin ich über den genialen Blogartikel von Mike Cardwell gestolpert. Er hat eine Idee gehabt, wie es möglich wird herauszufinden, ob Benutzer auf Facebook und Konsorten eingeloggt sind oder nicht.

Umgesetzt in eine kleine Library läuft es als Proof-of-Concept auf Firefox und sieht folgendermassen aus:

Loggen Sie in Gmail, Facebook, Twitter oder Digg ein um Resultate zu sehen.
Sind Sie nicht abgelenkt?

Neuer Check

Grundsätzlich wäre es ja ganz einfach: Man schaut vom Client-Browser aus, ob man Zugriff auf Bilder oder Seiten hat, die nur für Eingeloggte zur Verfügung stehen. Leider gestaltet sich dieser simple Ansatz nicht so einfach, weil mit Ajax nur zur Domain der Website verbunden werden darf und nicht an einen x-beliebigen Ort. Ein Proxy hilft auch nicht weiter, da es die Cookies des Browsers braucht, und die werden nur an Facebook, etc direkt abgeliefert.

Wie kann man also diese Restriktion umgehen?

Weiterlesen

PHP Funktionstests aus Netbeans mit Selenium

SeleniumIch hab da so ein Projekt, dass ich schon mehr als ein Jahr vor mir herschiebe: Eine Überarbeitung und Erweiterung einer bestehenden PHP-Applikation. Sie läuft eigentlich super, das Pflichtenheft ist gut und alle sind glücklich. Leider hat sich die „Erweiterung“ als Operation am Herzen herausgestellt. Der ursprüngliche Cöder hat eine ziemlich komplexe Struktur erarbeitet, in der er sich selbst ab und zu verheddert hat. Das bedeutet, dass zuerst ganz hässliche Strukturbugs als solche identifiziert und gefixt werden mussten. Im Zuge dieser Operation habe ich so ziemlich jeden Teil der Software berührt, beflucht und daran herumgewurschtelt.

Um etwas Sicherheit und Stabilität in die Sache zu bringen hätte ich gerne Unit Tests gehabt. Bei diesem Kludge sind aber so viele Komponenten beteiligt, dass es nicht so einfach klappt. Der Hammer musste eine Nummer grösser gewählt werden: Selenium.

Mit Selenium verbindet mich eine unerwiderte Liebe. Immer wieder versuche ich an das Tool heranzukommen, immer wieder scheitere ich irgendwie weil ich abgewiesen werde. So auch zu Beginn dieses Anlaufs: Die Funktionstests laufen (vielleicht), finden es aber unangebracht sich irgendwie zu melden. Die Doku ist gut, aber nirgends wird ein Überblick vermittelt und man muss die fragilen Einzelteile mit gutem Zureden zueinander bringen.

Weiterlesen

Ein Programmierwettbewerb vom Linux Magazin

Nachtrag vom 19.08.2010: Ich wurde Opfer von „mod_security“, damit konnte ich keine Artikel mehr editieren, weil ich immer ein „404 Not Found“ präsentiert bekommen habe. DAS ist doch ein Heuler…

Nun, ich habe eine neue Version des Clients unten hingehäng: Stabiler, so dass er auch eine ganze Nacht über viele Stunden und Verbindungsprobleme hinweg weiterspielt.

WuerfelOh nein. Frisch erkältet wollte ich mich auf die Arbeiten stürzen, die eigentlich schon letztes Jahr hätten erledigt sein müssen, und nun das: Das Linux-Magazin veranstaltet einen Wettbewerb für Programmierer! Die Details sind in der Ausgabe 09/10 (auch online) beschrieben und es gibt eine Wettbewerbs-Seite dazu.

Grundsätzlich geht es darum, dass zwei Programme gegeneinander würfeln. Wer zuerst 50 Punkte oder mehr erreicht, hat gewonnen. Ist man am Zuge, so kann man entweder Würfeln oder „Save“n. Bei einem Wurf werden die Augen zu der eigenen Punktzahl hinzugezählt, ausser man würfelt eine 6, dann werden alle Punkte seit dem letzten Save wieder abgezogen. Bei einem Save werden die Punkte gespeichert und der Gegner ist am Zuge.
Weiterlesen

Zeitintervalle (Erledigungsfristen) in PHP

timeIch bin wiedereinmal am Coden und irgendwie scheine ich mit fortschreitendem Alter auf immer mehr ungelöste Probleme zu treffen. In der aktuellen Applikation geht es darum, dass Erledigungsfristen als Intervalle eingegeben werden können: Beispielsweise muss eine Aufgabe immer in 2 Monaten, 1 Woche und 4 Sekunden nach dem Erfassen erledigt sein. Daraus ergeben sich ein Strauss an Problemen (oder ein Problemkorb, wie man neuerdings zu sagen scheint. Was für eine schreckliche Vorstellung, nur FRÜCHTE-Körbe finde ich noch abschreckender):

  • Jemand muss die Aufgabe erledigen. Zum Glück ist das ein PAL (Problem anderer Leute) 🙂 .
  • „In einem Monat“ und „In einem Jahr“ kann nicht trivial in Sekunden umgerechnet werden. Irgendein Soziopath hat mal bestimmt, dass nicht alle Monate und Jahre die gleiche Anzahl Tage haben. Ganz zu schweigen von Schaltsekunden und Sommer-/Winterzeit.
  • Aus hier zu verschweigenden Gründen habe ich weder Lust noch Zeit ein GUI mit 7 Textfeldern für Jahre, Monate, Wochen, Tage, Stunden, Minuten und Sekunden aufzubauen.

Ich habe mich für ein simples Eingabefeld entschieden, in denen das Erledigungsintervall in der Form „Zahl Zeitbezeichner Zahl Zeitbezeichner …“ eingegeben werden kann. In obigem Beispiel wäre das: „2m 1w 4s„.

Weiterlesen

Sortierung eines Dropdownwidgets in Symfony ändern (Doctrine)

Frameworks sind toll. Man kann in vielen tausend Zeilen das tun, wozu man früher nur ein paar Statements gebraucht hat… Oder war es umgekehrt? Hier etwas Symfony spezifisches, das mir ein paar gefärbte Haare gekostet hat, weil ich sie mir ausreissen musste.

Das Problem

Es sei gegeben eine dreistufige Hierarchie: Kategorien → Fächer → Kurse. Es sind ganz saubere 1:n-Beziehungen: Jedes Fach gehört zu einer Kategorie und ein Kurs ist genau ein durchgeführtes Fach.

Folgt man dem (wirklich guten) Symfony-Tutorial, so wird an Tag 3 ganz unten gezeigt, wie aus der Tabellendefinition automatisch ein Formular generiert wird. Clevererweise werden für 1:n-Beziehungen Dropdowns generiert.

Wenn ein neuer Kurs generiert wird, so soll man angeben können, was für ein Fach er unterrichtet. Standardmässig werden Fächer wie folgt angezeigt:

  1. Ein Kategorienkürzel („I“ für Informatik, „A“ für Ausdruckstanz (haha), …)
  2. Einer „Nummer“ (normalerweise „01“ bis „15“ oder so)
  3. Dem Namen des Fachs

Das Fach braucht für seine Darstellung also das Kürzel aus dem Kategorien-Model (bin ich der Einzige der bei Models immer grinsen muss?).

Das generierte Dropdown zeigt die Namen der Fächer falsch und alles Kreuz und Rüben durcheinander an. Ha, nun ist guter Rat teuer.

dropdownstart

Lösung

Namen der Fächer

Das geht noch relativ einfach: Jedes Model besitzt eine __toString() Methode, die überschrieben werden kann:

# File: lib/model/doctrine/modelnamefach.class.php
[...]
  public function __toString() {
        return sprintf('%s %s %s', $this->FreifachNgKategorie->getKuerzel(), $this->getNummer(), $this->getName());
  }
[...]

Und tatsächlich:

dropdownbefore

Sortierung

Ich hätte mir eigentlich gewünscht, für jedes Model eine Defaultsortierung angeben zu können. Aber da nicht Weihnachten ist, werden meine Wünsche nicht erfüllt. Da hilft nur Quellcodestudium und glückliches Zusammenpuzzeln der Doctrine Anleitung, dem Symfony-Doctrine Buch und der API-Dokumentation (scrollen bis Doctrine): Beispiele gibts leider fast keine.

Nungut: Das Formular wird automatisch generiert (lib/form/doctrine/base/), darum sollte hier nur wer was ändern, der per Stunde bezahlt wird und das nach jeder Generierung wieder neu tun kann :). Für uns Andere müssen wir das Choice in der Kinderklasse hacken und eine table_method reinbringen, die die Einträge nach unserem Gusto liefert:

# File: lib/form/doctrine/modelnamekurs.class.php
[...]
  public function configure()  {
      $this->setWidget('freifach_id', new sfWidgetFormDoctrineChoice(array('model' => 'FreifachNgFreifach', 'table_method' => 'retrieveFreifachOrderedByCategory')));
  }
[...]

Diese table_method muss auch noch codiert werden. Und das in der Tabellenklasse der Fächer (dochdoch, wenn man sich das überlegt ist es ganz logisch *irres_grinsen*).

# File: lib/model/freifachmodelnameTable.class.php
[...]
    function retrieveFreifachOrderedByCategory() {
        $q=Doctrine_Query::create()->from('FreifachNgFreifach f')->innerJoin('f.FreifachNgKategorie k')->orderby('k.kuerzel, f.nummer, f.name');
        //echo $q->getSql();
        return $q;
    }
[...]

Toll, und dann klappts auch mit dem Dropdown:

dropdownordered

Individuelle, geschützte und dynamisch erstellte SVN-Repositories

facesDas Problem: Es sollen individuelle SVN Verzeichnisse erstellt werden. Ein Bereich also, in dem eine Person beliebig viele Repositories erstellen kann, die nur von ihr selbst angesehen und bearbeitet werden können. Mann könnte jetzt meinen – vorallem mit viel jugendlichem Leichtsinn – das sei ein einfaches, allgemein bekanntes Problem. Pustekuchen! Das muss ein ganz fremder wohl ganz abstruser Wunsch sein.

Verschiedene Dinge schränken die Chance ein, eine einigermassen vernünftige und allgemeine Lösung zu finden 🙁 .Doch der Reihe nach. Was ist gegeben:

  • Ein wunderbarer Server, mit SSL und allem.
  • Das Einrichten eines neuen Users sollte ungefähr 0,01 Sekunden menschlicher Arbeitskraft bedürfen.
  • Die Benutzer sind in einem Active Directory gespeichert.
  • Apache2, PHP, und alles was man will ist installiert.

Einschränkende Dinge

Die grösste Peinlichkeit ist die Direktive SVNParentPath (von mod_dav_svn). An sich sollte die Direktive ein Basisverzeichnis für die SVN-Repositories angeben aber:

  • Der Parameter (das Verzeichnis) ist absolut undynamisch. Er nimmt keine Umgebungsvariable, Regexp oder sonst was, sondern ganz stur nur einen festen Pfad.
  • Kann nicht in einer .htaccess Datei gesetzt werden

Gna! Das ist echt 2002! So wird das nichts mit dynamisch erstellten SVN-Verzeichnissen. Aus reinem Masochismus hane ich verschiedene Lösungen wie mod_rewrite, symbolischen Links, LocationMatch und alles was sonst noch so Zeit kostet ausprobiert. Keine Chance…
Erschwerend hinzu kommt, dass sich SVN überhaupt nicht mit der Alias Direktive anfreunden kann und konsistent stur auf eine jungfräuliche Location beharrt: Der Versuch wird mit einem Repository moved permanently, please relocate belohnt.

Die Krönung: mod_authz_path

mod_authz_path klingt vielversprechend. An Hand des Pfades sollte der User und das Realm festgelegt werden können. Jaaa, aber nicht als Location, sondern nur als Pfad (<Directory> im Apache), wie man mit dem Debuggen feststellen kann:

AuthNamePathMatch (.+) "Persoenliches SVN $1"
AuthzUserPathMatch (.+) $1

Mit dem Pfad findet sich aber – wie schon weiter oben geflucht – das SVN nicht zurecht.

Ganz nebenbei hat mich dieses Modul etwa viertausend Stunden gekostet. Wenn es geladen wird, funktioniert authnz_ldap nicht mehr richtig. Nicht überhaupt nicht, sondern einfach das require Statement spielt verrückt und verhält sich unberechenbar. Nach langer Zeit habe ich dann das auch gemerkt :irre: . Aber erst nachdem ich unter viel Wehklagen in einer Panik die anderen Server getestet habe, ob die Authentifizierung dort auch nur scheinbar funktioniert.

Die Lösung

Nach ein paar Jahren die Lösung: Gutes, altes PHP.

Auf dem SSL-Port des Servers ist eine Website, auf der sich die User mal als erstes über Apache am Active Directory authentifizieren müssen. Dadurch habe ich Zugriff auf den Benutzername mittels $_SERVER['AUTHENTICATE_SAMACCOUNTNAME']. Loggt sich jemand ein den wir noch nicht kennen, dann:

  1. Wird der svn-Grundpfad erstellt, wenn er noch nicht existiert.
  2. Wird eine Apache-Konfiguration für diesen neuen Grundpfad erstellt, die das ParentPath und den entsprechenden User beinhaltet.
  3. Wird Apache neu gestartet → und der Benutzer währenddessen zum Warten gebracht. Das ist leider notwendig zum Einlesen der Konfigurationserweiterung.

Die neue Konfigurationsdatei wird „lokal“ erstellt und in der Grundkonfiguration /etc/apache2/sites-available/isvn.myhost.ch eingebunden:

        Include /data/www/isvn.myhost.ch/config/svn.conf

PHP Code für die neuen Benutzer:

  $svn_user=strtolower($_SERVER['AUTHENTICATE_SAMACCOUNTNAME']);
  if(!$svn_user) { echo "Hacker, you will be disintegrated!"; exit(1); }
  $svn_root=SVNROOT."/".$svn_user;
  if(!is_dir($svn_root)) { // NEW USER
        // Make svn Parent Path
        mkdir($svn_root);
        // Write Config
        $fh = fopen(SVNCONF, 'a') or die("can't open file");
        $svnDirective =<<<EOD

<Location /svn/lp/$svn_user>
        DAV svn
        SVNParentPath /data/svn/lp/$svn_user
        AuthName "Persoenliches SVN von $svn_user"
        AuthType Basic
        AuthBasicProvider ldap
        AuthLDAPBindDN ldapanonymous@myhost.local
        AuthLDAPBindPassword g3h31M3Sp+
        AuthLDAPUrl ldap://adc1.myhost.local/ou=Accounts,dc=myhost,dc=local?sAMAccountName?sub
        require ldap-user $svn_user
</Location>

EOD
;
        fwrite($fh, $svnDirective);
        fclose($fh);

        // Make th User wait
        echo "<html><head><title>SVN wird eingerichtet</title>\n";
        echo '<meta http-equiv="refresh" content="5; URL=https://isvn.myhost.ch">'."\n";
        echo "</head><body>".implode(" ", explode(".",$_SERVER['AUTHENTICATE_SAMACCOUNTNAME'])).": Ihr SVN Grundverzeichnis wird erstellt. Bitte warten Sie.</body></html>";

        // Restart Apache
        exec('echo "/usr/bin/sudo /etc/init.d/apache2 restart" | /usr/bin/at now');
        exit();
  }

Damit der User www-data den Apache restarten darf, muss ihm das per /etc/sudoers erlaubt werden:

www-data ALL=NOPASSWD: /etc/init.d/apache2

Dass die Berechtigungen auch für alle benötigten Zusatzdateien reichen, machen wir den Trick mit dem at.

Somit haben wir also eine hochindividuelle Konfigurationsdatei für alle User und Ihr SVN Grundverzeichnis. Die PHP-Seite ermöglicht es, SVN-Repositories anzulegen und zeigt auch gerade den annektierten Platz an:

Die SVN-Steuerkonsole

Die SVN-Steuerkonsole

Die Quellen

Hier sind die PHP-Quellen. Für weitere Details stehe ich gerne zur Verfügung.

Ich will sie alle: Informatikzertifikate und -kätchen

CertificationEigentlich ist ja heute der Weltuntergang (made in Switzerland) angesagt. Ob er schon stattgefunden hat kann man übrigens mit der Website Has the Large Hadron Collider destroyed the world yet? überprüfen.

Diese meine letzten Stunden möchte ich nutzen, um über die Erfahrungen mit Zertifikaten und der Vorbereitung darauf festzuhalten. Was tut man, wenn man im Leben weiterkommen will? Richtig, man heiratet gut und reich. Was macht man wenn man hässlich ist? Richtig: Eine Ausbildung. Was, wenn man dafür zu blöd ist und die Schnauze noch voll hat von einer psychosozialaktiven biologisch abbaubaren lasstunseinanderspürenden Lehramtsausbildung? Richtig: Ein Zertifikat. Und wenn man viel zu viel Schlaf und Zeit hat? Richtig: Viele Zertifikate… :denken:

Ausserdem macht der Adrenalinkick süchtig 😀 … Darum hier die Erfahrung mit ECDL, LPIC, ZCE und Kollegen…
Weiterlesen

Automatisches und mehrfaches submitten von Formularen

Wirre Gedanken

FormularDieser Beitrag ist dem Titz gewidmet. Dem Titz, der sich aufgeregt hat :pirate-grumble: , obwohl es gar nicht nötig gewesen wäre und der sich nun mit einer gewissen Teilnehmerredundanz anfreunden muss. Hauptsache ist doch, das PHP fliesst und die Variablen bleiben sauber… Und ein Bierchen würde ich auch noch springen lassen :bier: …

Eine wichtige Frage zu Beginn: Wieso sollten wir denn automatisch und mehrfach Formulare submitten wollen? Hmm, um den Titz zu ärgern? Weil wir es können? Weil man manchmal tun muss, was man tun muss? Weil man seine 84 Kinder an einem elektronischen Fussballturnier anmelden will :laola: (hat eigentlich schon jemand bemerkt, dass ich neue Smilies und unglaublich Freude daran habe?)? Oder weil man über einen Wettbewerb gestolpert ist, der ebendies nicht verbietet (also das Mehrfachsubmitten, nicht das Kinderanmelden oder die neuen Smilies) und der kein Captcha hat (vielleicht, weil es der Titz vergessen hat)?

Nachtrag 31.07.2008: Schenken mir doch so unglaublich nette Mitarbeiter einer Versicherung heute morgen am Bahnhof einen Müsliriegel. Und auf diesem eher gesunden Teil hat es, ja rate, oh wissbegieriges Volk, einen Wettbewerb. Ob der Titz wohl am Abend für eine andere Firma weitercoded? Ich werde mich auf jeden Fall während der Zugfahrt mal damit befassen :computer: .

Vorgehen

Erster Schritt: Selenium IDE

Man könnte nun wie wild losprogrammieren, oder aber einen einfacheren Weg wählen. Ein guter Startpunkt für automatisiertes Browsen generell ist die Selenium IDE. Dieses geniale Teil für den Firefox zeichnet wie ein Macrorecorder alles auf, was im Browser gemacht wird. Hat man die Teilnahme beim Wettbewerb einmal so aufgezeichnet, so müssen nur noch die click’s, die das Form absenden, durch clickAndWait’s ersetzt werden, damit vor dem Weiterausfüllen (bei mehrseitigen Formularen) auf die neue Seite gewartet wird. Es empfiehlt sich, eine Abschlussüberprüfung als letzten Schritt hinzuzufügen, um um kontrollieren zu können, ob die Kinder erfolgreich angemeldet wurden (markieren des „Dankeblabla“, dann rechte Maustaste und assertTextPresent).

Ein Wettbewerb über 2 Seiten. Aufgezeichnet und bearbeitet mit der Selenium IDE.

Ein Wettbewerb über 2 Seiten. Aufgezeichnet und bearbeitet mit der Selenium IDE.

Diesen Testcase kann man nun abspeichern und eigentlich immer wieder ausführen. Ein Klick reicht und Firefox rasselt alles schön durch. Den Namen leicht verändern kann man durch Editieren des Skripts…

Für den zweiten Schritt sollte man das Testscript als PHP exportieren.

Zweiter Schritt: Selenium RC

Die Selenium RC Komponente kann den Browser fernsteuern und so ferngesteuert am Wettbewerb teilnehmen. Um sie unter Linux Debian zum Laufen zu kriegen, war ein Bisschen Gemurkse notwendig.

Weiterlesen