webcodr

Services ftw!

Wer kennt das nicht? Man findet eine nette Software-Bibliothek in einer bestimmten Sprache, die vom Server der eigenen Web-Applikation nicht unterstützt wird oder nur sehr umständlich auf andere Weise genutzt werden kann.

So erging es mir mit Tools für Markdown-Rendering und server-basiertem Syntax-Highlighting. Zwar habe ich dafür ja im Oktober die Composer-Pakete SilexMarkdown und Radiant geschrieben, die beide auf bereits vorhandenen Bibliotheken fußen.

Ich war mit beiden nie recht glücklich. Für Ruby und Python gibt es viel schönere, wesentlich umfangreichere Lösungen:

  • Pygments ist ein Python geschriebener Syntax-Highlighter, der nahezu jede relevante Sprache unterstützt – selbst esoterische Merkwürdigkeiten wie Brainfuck.

  • Redcarpet wurde in Ruby verfasst und bietet einen sehr leicht erweiter- und modifizierbaren Markdown-Renderer.

Gerade für ein Blog-System wie CodrPress liegt es nahe, beide zu kombinieren und damit zumindest teilweise GitHub flavoured Markdown zu unterstützen.

Wie bekomme ich also drei Programmiersprachen unter einen Hut, ohne dass CodrPress nur auf angepassten Server-Konfigurationen läuft? Ganz einfach: Services!

Pygmentizr

Pygmentizr ist logischerweise in Python geschrieben, um Pygments nutzen zu können.

Per POST-Anfrage auf die verlinkte URL erreicht man den eigentlichen Service, der als Parameter die Sprache und den Quelltext erwartet. Zurück kommt HTML, das nur noch per CSS hübsch gemacht werden muss.

Ein entsprechendes Stylesheet für den bekannten Monokai-Stil ist auf der Seite verlinkt oder im GitHub-Repository zu finden.

Pygmentizr bei GitHub

Amplifyr

Amplifyr nutzt Redcarpet und bindet Pygmentizr als Syntax-Highlighter ein.

Wie Pygmentizr lässt sich Amplifyr per POST-Anfrage ansprechen und gibt den in HTML konvertierten Markdown-Quelltext zurück.

Amplifyr bei GitHub

Beide Dienste laufen auf der Cloud-Plattform Heroku.

CodrPress-Integration

Um beide Services in CodrPress nutzen können, habe ich in SilexMarkdown ein paar Umbauten vorgenommen. Beim Registrieren des Service-Providers in einer Silex-Applikation, lässt sich nun ganz einfach übergeben, ob der eingebaute Renderer samt Radiant oder Amplifyr genutzt werden soll. Eine entsprechende Anleitung findet sich in der ReadMe-Datei des SilexMarkdown-Repositories bei GitHub.

Beide Dienste sind bei Heroku untergebracht und kosten mich keinen Cent. daher gebe ich die Nutzung für jeden frei. Viel Spaß!

Array almighty

Wenn man sich zulange nur mit PHP beschäftigt vergisst man schnell, dass man oft Dinge tut, die kaum in andere Sprachen übertragbar sind.

Letztens habe ich mir Scala etwas näher angesehen. Kurz am Rande: eine schöne Sprache, wenn auch die verschiedenen Syntax-Modi etwas verwirrend oder zumindest recht gewöhnungsbedürftig sind.

Vorteil PHP

Scala bietet wie fast jede andere typisierte Sprache verschiedene Listen-Klassen für diverse Nutzungsfälle. In PHP gibt es das nicht. Man hat sein Array, das jederzeit veränderlich ist, jede noch so wilde Mischung von Datentypen akzeptiert und assoziative Schlüssel erlaubt. Es ist einfach umgemein praktisch.

Vorteil Scala

Da PHP leider weit davon entfernt ist vollständig objekt-orientiert zu sein und darum ein Array leider keine Objekt ist, kann man Arrays nur mit diversen Funktionen bearbeiten.

Zwar funktioniert das einwandfrei, ist aber umständlich. Ein Array-Objekt, das entsprechende Methoden bietet, die sich am besten auch noch verkettet aufrufen lassen, wäre doch toll.

PHP goes Scala/Java/Objective-C

Daher habe ich mich ans Werk gemacht und die bereits existierende Klasse IterateableList in MongoAppKit in drei neue Klassen des Namespaces \MongoAppKit\Collection aufgeteilt: MutableMap und ArrayMap.

Die Namen orientieren sich an ihren Pendants in Scala, Java oder auch Objective-C. Während alle die SPL-Interfaces Countable und IteratorAggregate implementieren, verwendet ArrayMap zusätzlich das Interface ArrayAccess und kann damit wie ein PHP-Array verwendet werden.

Außerdem implementieren alle drei die Magic Methods __get(), __set(), __isset() und __unset. Das erleichtert z.B. die Verwendung einer Liste in Twig, in dem keine Methode mehr angesprochen werden muss, um innerhalb eines Templates auf die Inhalte zuzugreifen.

Um diverse Array-Funktionen von PHP abzubilden implementieren alle drei die Methoden:

  • first(): gibt das erste Element der Liste zurück
  • last(): gibt das letzte Element der Liste zurück
  • reverse(): dreht die Reihenfolge der Elemente innerhalb der Liste um
  • each(): wendet eine Callback-Funktion mittels array_walk auf alle Elemente an, in der auch auf die Schlüssel zugegriffen werden kann
  • map(): wendet eine Callback-Funktion mittels array_map auf alle Elemente an
  • slice(): schneidet einen Teil der Elemente heraus und gibt sie in einem neuen Listen-Objekt zurück
  • filter(): filtert die Elemente einer Liste anhand einer Callback-Funktion und gibt das Ergebnis in einem neuen Listen-Objekt zurück

Fluent Interface

Um eine Verkettung von Methodenaufrufen zu ermöglichen gibt jede Methode, die sonst keinen Rückgabewert hätte, eine Referenz auf ihre Klasse zurück. Nur slice() und filter() geben eine neue Liste mit den herausgeschnittenen bzw. gefilterten Werten zurück.

Hier ein kleines Beispiel aus CodrPress, was man damit alles anstellen kann:

<?php

Post::where()->each(function($document) use ($app) {
    $md = $document->getProperty('body');
    $html = $app['markdown']->transform($md);
    $document->set('body_html', $html)->save();
});

Die statische Methode Post::where() liefert ohne Quert alle Posts als MutableMap-Objekt zurück. Auf die Rückgabe lässt sich sofort each() anwenden, das alle Elemente der Liste iteriert und die definierte Closure auf jedes Element einzeln anwendet.

In diesem Fall wird das rohe Markdown aus dem Feld body in HTML transformiert und im Feld body_html abgespeichert.

Fazit

MutableMap und seine Sub-Klassen sparen viel Schreibarbeit durch ein simples und komfortables Fluent Interface – einer Vorgehensweise, der in PHP leider viel zu wenig Beachtung geschenkt wird.

Download

Die drei Klassen sind nicht länger Teil von MongoAppKit. Ich habe sie in ein separates GitHub-Repository und Composer-Paket ausgelegt, um eine unkomplizierte Nutzung ohne MongoAppKit zu ermöglichen. Viel Spaß!

PythonPress

Da eine neue Sprache nicht genug ist, beschäftige ich mich neben Ruby neuerdings auch noch mit Python.

Als Lernprojekt setze ich aktuell CodrPress als Python-Version um. Natürlich will ich das nicht das Rad neu erfinden, daher setze ich auf zwei Frameworks, dank denen ich sehr schnell ein vorzeigbares Ergebnis zusammenbauen konnte:

  • Flask ist ein Micro-Framework vergleichbar mit Sinatra (Ruby) oder Silex (PHP). Es kümmert sich also um alles, was man braucht, um eine Website zu bauen. Vom Routing bis hin zur Template Engine (Jinja2).

  • MongoEngine bietet einen ODM (Object Document Mapper) vergleichbar mit Mongoid (Ruby) oder meinem eigenen Projekt MongoAppKit in PHP.

Routen-Definitionen mit Flask

from flask import Flask

app = Flask(__name__)

@app.route('/hello/<name>')
def hello(name):
    return 'Hello %s!' % name

app.run()

Zu Beginn wird die Klasse Flask aus dem Package flask importiert und anschließend eine Instanz erstellt.

Im Gegensatz zu PHP oder anderen C-Syntax-Sprachen kennt Python das Schlüsselwort new nicht. Um ein neues Objekt zu instanziieren reicht es den Klassennamen samt den Klammern und ggf. den Constructor-Argumenten zu schreiben.

Es folgt die Routen-Definition. Variable Werte werden in spitze Klammern gesetzt und der anschließenden Methode mit gleichem Namen als Parameter übergeben.

Wie auch in Silex oder Sinatra wird der Rückgabewert einer Routen-Methode zurück an den Browser geschickt. In diesem Fall ist das nur ein simpler String-Wert.

Templates in Flask

Flask nutzt die Template Engine Jinja2. Wer aus der PHP-Welt Twig kennt fühlt sich sofort heimisch. Die Sprachelemente sind nahezu identisch.

Datei: ./templates/hello.html

<h1>Hello {{ name }}!</h1>  

In obiger Route müsste die Methode nun so aussehen:

from flask import Flask, render_template
...
def hello(name):
    return render_template('hello.html', name = name)
...

Nicht vergessen: render_template muss zusätzlich importiert werden!

MongoEngine

from mongoengine import *

connect('test')

class Post(Document):
    _id = ObjectIdField()
    created_at = DateTimeField()
    published = BooleanField()
    title = StringField()
    body = StringField()

Der Aufruf von connect stellt eine Verbindung zur Datenbank test her. Da keine Verbindungsdaten angegeben werden, geht MongoEngine automatisch von einem lokalen MongoDB-Server auf dem Standard-Port aus.

Die Klasse Post ist eine Sub-Klasse von Document aus MongoEngine. Anschließend werden die Felder der Klasse definiert. MongoEngine stellt für jeden von MongoDB unterstützten Datentyp entsprechende Klassen zur Verfügung.

Sofern nicht über das Attribut meta eine andere Collection definiert wird, greift MongoEngine auf den Klassennamen in Kleinbuchstaben als Collection zu.

Um ein neues Dokument von Post zu erstellen und zu speichern, reicht schon folgender Code:

post = Post()
post.published = True
post.title = 'Hello World!'
post.body = 'Hallo, ich ein Test.'
post.save()

Abfragen mit MongoEngine

Als vollständiger ODM bietet MongoEngine natürlich auch die Möglichkeit vorhandene Daten abzufragen. In folgendem Beispiel werden die letzten zehn veröffentlichten Einträge absteigend nach der Erstelldatum sortiert, in ein Array geschrieben.

posts = Post.objects(published = True).order_by('-created_at').limit(10)

Abfragen erfolgen statisch, daher ist keine Instanz nötig. Die Methode objects() enthält die Bedingungen, also in diesem Fall, dass ein Eintrag veröffentlicht wurde. order_by() erwartet den Feldnamen mit der Sortierrichtung als Präfix. Hierbei steht + für aufsteigend und - für absteigend. Zu guter letzt wird das Ergebnis mit limit() auf 10 Dokumente eingeschränkt.

Mit diesem Wissen lässt sich nun ganz schnell eine Basis-Applikation bauen, die aus der vorhandenen CodrPress-Collection Einträge ausliest und anzeigt.

Der bisherige Stand ist natürlich bei GitHub.

CodrPress

Ich bin mal wieder so wahnsinnig und arbeite an einem Blog-System. Diesmal will ich das Rad aber nicht neu erfinden und ein zweites Wordpress bauen. Stattdessen orientiert sich CodrPress an Schnitzelpress.

Da Schnitzelpress auf Ruby basiert und primär für den Einsatz auf Heroku ausgelegt ist, habe ich mich dazu entschieden mit CodrPress quasi eine PHP-Version von Schnitzelpress zu entwickeln.

Natürlich ist das wieder mal einfacher gesagt als getan, vor allem da es für diverse Ruby Gems, die Schnitzelpress nutzt, in der PHP-Welt kaum brauchbaren Ersatz gibt.

Mit Redcarpet und CodeRay hat Ruby zwei wundervolle Gems, die sich um Markdown-Rendering bzw. Syntax-Highlighting kümmern.

CodrPress basiert auf meinem Projekt MongoAppKit, das widerrum auf Silex sowie Twig setzt und seine Abhängigkeiten mit Composer regelt. Keine der PHP-basierten Lösungen, um diese zwei Ruby Gems zu ersetzen, bietet Composer-Unterstützung an und die Strukturen sind z.T. auch nicht PSR-0-kompatibel, so dass ein Autoloading der Klassen nicht möglich ist.

Daher habe ich zwei neue Projekte aus der Taufe gehoben, die genau diesen Mangel beseitigen:

SilexMarkdown

Da ich keine Lust und Zeit habe, selbst einen Markdown-Renderer zu schreiben, basiert SilexMarkdown auf php-markdown von Michel Fortin.

Ich musste es erstmal in brauchbare Struktur bringen, da das Original leider weder Namespaces nutzt und sogar zwei Klassen in einer Datei besitzt.

SilexMarkdown stellt nun eine Service-Prodiver-Klasse für Silex und eine entsprechende Twig-Extension zur Verfügung. Dazu wurde es noch mit einer Unterstützung für Code-Blöcke angereichert, um Syntax Highlighting wie in GitHub nutzen zu können.

Radiant

Die Kern-Komponente von Radiant ist ebenfalls nicht auf meinem Mist gewachsen und stammt aus dem Projekt Nijikodo von Clint Campbell.

Immerhin war die Grundlage schon mal PSR-0-kompatibel und damit auch relativ leicht Composer-tauglich zu machen.

Meine Arbeit bestand zum Großteil darin, entsprechende Unit-Tests zu schreiben und einige Fehler zu beseitigen und es in SilexMarkdown einzubinden.

Qualität

Alle genannten Projekte, also MongoAppKit, SilexMarkdown, Radiant und CodrPress werden mittels PHPUnit laufend von mir und automatisiert via Travis CI getestet. Abgesehen von SilexMarkdown beträgt die Code-Coverage zwischen 70 - 90%.

Style

Aktuell ist CodrPress mit dem vollständigen Twitter Bootstrap versehen, um auch in der Entwicklunsphase ein halbwegs ahnsehnliches Design zu haben. Später werde ich nur ein paar Komponenten aus Bootstrap nutzen, z.B. das Grid und die responiven Fähigkeiten.

Für das Syntax-Highlighting habe ich ein Farb-Theme basierend auf meinem Farbschema von PhpStorm geschrieben, das auch Radiant beiliegt. Dank einer recht einfachen Struktur kann man sich auch sehr schnell ein eigenes Theme zusammenstellen.

Ausblick

Die Frontend-Funktionen von CodrPress sind mit einer gefüllten Datenbank (Schnitzelpress-kompatibel) schon nutzbar. Homepage, Einzeldarstellung von Einträgen, eigene Seiten und das Markdown-Rendering mit Syntax-Highlighting funktionieren soweit einwandfrei.

Als nächstes werde ich einem Admin-Bereich und anschließend einem ansprechenden Design widmen.

Menlo Park, start your photocopiers ...

… oder warum Software-Patente und Patentkriege scheiße sind.

Gestern habe für die Share-Funktionen von Twitter, Google+ und Facebook jeweils ein Modul nach dem CommonJS-Standard gebaut, um sie in meinem privaten Weblog zu nutzen.

Daran ist nun nichts besonders, wenn ich nicht eine kleine Entdeckung gemacht hätte. Offenbar hat Facebook den nötigen JavaScript-Code von Twitter kopiert oder Twitter von Facebook.

Quelltext vom Twitter

!function(d, s, id) {
	var js, fjs = d.getElementsByTagName(s)[0];

	if(!d.getElementById(id)) {
		js = d.createElement(s);
		js.id = id;
		js.src = "//platform.twitter.com/widgets.js";
		fjs.parentNode.insertBefore(js, fjs);
	}
}(document, "script", "twitter-wjs");

Quelltext von Facebook:

(function(d, s, id) {
	var js, fjs = d.getElementsByTagName(s)[0];
	if(d.getElementById(id)) return;
	js = d.createElement(s);
	js.id = id;
	js.src = "//connect.facebook.net/de_DE/all.js#xfbml=1";
	fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

Zwecks der Lesbarkeit habe ich die Funktionen entsprechend formatiert.

Selbst Nicht-Programmierern dürften die Ähnlichkeiten kaum entgehen. Die Variablennamen sind identisch und sogar die Art der URL-Angabe ohne Protokoll. Selbst das if-Conditional und damit die Methode das externe JavaScript nicht zweimal einzubinden, stimmen überein – nur die Schreibweise ist etwas anders.

Aus meiner Sicht geht dieses Vorgehen das vollkommen in Ordnung. Man muss nicht ständig das Rad neu erfinden. Gerade Programmierer tun das sehr gerne, obwohl es nur selten notwendig ist.

Twitter und Facebook sind Technologie-Vorreiter, neben Google die zwei wichtigsten im gesamten Netz – warum sollten sie also nicht gegenseitig voneinander profitieren? Auch wenn es nur um einen Code-Schnippsel geht, der externe JavaScripts lädt.

Andere Firmen (Hallo, Oracle!) holen selbst bei wesentlich geringeren Quelltext-Ähnlichkeiten gleich die Klage-Keule raus. Durch die Möglichkeit in den USA Patente auf Software zu bekommen, ist sowas sogar oft von Erfolg gekrönt …

Ich bin kein Verfechter von Open Source, auch wenn ich es grundsätzlich für eine gute Sache halte. Noch bin ich der Meinung, dass Software ein Allgemeingut wäre und jedem kostenlos zur Verfügung stehen müsse.

Jedem Entwickler muss das Recht zustehen, sein Produkt zu verkaufen und es schützen zu dürfen. Im Fall von Trivial-Patenten, geht es nicht mehr darum.

Man will nur noch der Konkurrenz schaden und Geld rausholen, obwohl man selbst oft mehr als genug hat. Firmen werden gekauft, um an die Patente zu kommen und anschließend andere Firmen mit Klagen zu überziehen.

Egal, ob Apple, Samsung, Motorola (Google), Nokia oder sonst wer. Hört endlich auf damit! Keiner eurer Kunden will Import-Verbote, absurd hohe Patentabgaben für verkaufte Geräte oder sonstige Auswüchse euer Advokaten-Armeen.

Aus Apples Sicht ist Android ein geklautes Produkt. Objektiv gesehen kann man dem sogar in Teilen zustimmen. Nur: na und?

Apple hat gute Ideen, Google hat gute Ideen. Nutzt sie, um euch gegenseitig zu verbessern und stellt diese lächerlichen Grabenkämpfe ein, die euch letztlich mehr schaden als nützen.

Responsive Bilder mit WordPress

Aktuell wage ich erste Gehversuche mit responsiven Layouts in meinem WordPress-Theme. Ziel der Übung ist ein smartphone-taugliches Layout. Leider macht einem WordPress die Arbeit bei Bildern unnötig schwer.

Automatische Bildskalierung mit CSS

Bilder müssen in responsiven Layouts automatisch mit der Breite des Anzeigegeräts skalieren. Feste Breiten würden hier zwangsläufig zu Darstellungsproblemen führen. Das klingt nun komplizierter als es ist. Mit drei Zeilen CSS lassen sich Bilder abhängig von der Breite ihres Eltern-Elements automatisch skalieren.

img {
	max-width: 100%;
}

Ein Bild darf also maximal so breit werden, wie seine vorgesehene Weite. Schrumpft das Eltern-Element durch Verkleinern des Viewports, wird das Bild automatisch mitskaliert. Wir müssen uns also um nichts weiter kümmern, da die Browser den Rest erledigen.

Problemfall WordPress

Leider klappt die automatische Skalierung in WordPress nicht. Wenn man in einem Beitrag Bilder einfügt, setzt WordPress automatisch das width- bzw. height-Attribut auf das img-Element. Sobald auch nur eines von beiden gesetzt ist, wird eine automatische Größenanpassung verhindert. Die Attribute müssen also weg.

Wie immer, gibt es dafür zig verschiedene Möglichkeiten. Beispielsweise könnte man die überflüssigen Element-Eigenschaften per jQuery-Einzeiler entfernen. Wirklich schön ist das aber nicht. Es wäre doch viel besser, wenn man WordPress dazu bringen könnte, den Quelltext gleich ohne width und height auszuliefern.

Dank des Hook-Systems in WordPress geht das mit ein paar Zeilen Code in der Datei functions.php des Themes:

add_filter('the_content', 'removeImageDimensions');

function removeImageDimensions($html) {
	return preg_replace('/(width|height)=\"\d*\"\s/', '', $html);
}

Die Funktion removeImageDimensions() entfernt per regulärem Ausdruck unsere unerwünchten Gäste width und height. Mittels add_filter() wird der WordPress-Funktion the_content() (gibt den Inhalt eines Beitrags aus) unsere neue Funktion als Ausgabefilter zugewiesen. WordPress führt nun bei jedem Aufruf von the_content() unsere neue Funktion removeImageDimensions() aus, die den Rückgabewert von the_content() entsprechend verändert.

Damit steht responsiven Bildbreiten nun nichts mehr im Weg.

Retina-Display-taugliche Icons mit CSS-Hintergrundbildern

Besitzer von Apple-Geräten mit Retina-Displays kennen das Dilemma: auf vielen Seiten sehen Hintergrundbilder, insbesondere Icons, reichlich unscharf aus. So erging es mir gleich doppelt mit meinem privaten Blog. Dank neuem iPad und MacBook Pro, vermatschen die Icons für externe Links.

Es gibt verschiedene Lösungsansätze für dieses Problem, beispielsweise Icons in vektorbasierten Formaten (SVG), die beliebig in jeder Pixeldichte skalieren können.

Da ich die verwendeten Icons nur als Bilder vorliegen habe, kommt diese Lösung nicht in Betracht. Dank CSS-Media-Queries ist das aber kein großes Problem.

Media-Queries?

Ein Media-Query ermöglicht es, innerhalb eines Stylesheets diverse Informationen zum aktuellen Anzeigegerät abzufragen. Dazu gehören unter Anderem die Minimal-Breite, die Ausrichtung (Portrait, Landscape), der Gerätetyp (Screen, Projection etc.), das Bildverhältnis oder in diesem Fall besonders wichtig, das Verhältnis zwischen vorhandenen und tatsächlich dargestellten Pixeln.

Was machen eigentlich Retina-Displays?

Retina-Displays verdoppeln die Auflösung, während die dargestellten Elemente gleich groß bleiben. Bei normalen Displays ist dieses Verhältnis 1:1. Eine Grafik mit 100 Pixeln Breite wird also mit 100 Pixeln auf dem Monitor dargestellt. Auf Apple-Geräten mit Retina-Displays verdoppeln sich die 100 Pixel auf 200 Pixel – das Verhältnis beträgt also 2:1.

Ist eine Grafik in der notwendigen Auflösung nicht verfügbar, wird das vorhandene Bild hochgerechnet und wirkt unscharf. Mit einem Media-Query können wir dem Browser eine höher aufgelöste Version zur Verfügung stellen, die genau das verhindert.

An die Arbeit

@media only screen and (min--moz-device-pixel-ratio: 2),
only screen and (-o-min-device-pixel-ratio: 2),
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-device-pixel-ratio: 2) {
	#content article .content p a.external {
		background-image: url("icons/external-url-32.png");
		background-size: 16px auto;
	}
}

Da die Media-Query-Eigenschaft min-device-pixel-ratio noch nicht vollständig in allen Browsern implementiert ist, setze ich die entsprechenden Präfix-Versionen vorher ein. So kann man sichergehen, dass die proprietäre als auch die standardisierte Eigenschaft greifen – je nach aktuellem Stand der Browser-Implementation.

Mozilla greift hier zu einer recht seltsamen Präfix-Syntax, während Opera und Webkit sich an das bewährte Schema halten. Ob und wann der Internet Explorer die Eigenschaft unterstützt, konnte ich bisher nicht herausfinden.

Wie oben schon beschrieben, ist das Verhältnis von tatsächlichen Pixeln zu dargestellten Pixeln 2:1, daher wird es in der Bedingung mit einer 2 angegeben.

Innerhalb des Media-Queries kann ganz normales CSS verwendet werden. Ich tausche nun einfach das bisherige Hintergrundbild (16 x 16 Pixel) durch eine größere Version (32 x 32 Pixel) aus und setze die Größe des Hintergrundbildes auf 16 Pixel. Ansonsten würde der Browser eine falsche Annahme treffen und das Bild auf 64 x 64 Pixel hochrechnen. Damit wäre alles für die sprichwörtliche Katz.

Das war’s schon. Sofern man ein passendes Gerät hat und der Browser die Media-Query-Eigenschaft unterstützt, bekommt man nun ein schön hoch aufgelöstes Icon zu sehen, das um Welten besser aussieht als der hochgerechnete Pixelmatsch.

PHP-Autoloader nach dem PSR-0-Standard

Wenn’s um das Schreiben eines Autoloaders in PHP geht, kochen viele Entwickler gern ihr eigenes Süppchen. Sofern nun mehrere gleichzeitig aktiv sind, kann das zu Problemen führen und ggf. sogar dafür sorgen, dass eine Alternative gesucht werden muss.

Die PHP Framework Interoperability Group (kurz FIG) hat sich dieses Problems angenommen und eine Spezifikation für Autoloader entwickelt, die Interoperabilität sicherstellt.

Der PSR-0-Standard besteht aus ein paar recht simplen Regeln, die sich sehr einfach umsetzen lassen und z.T. sicher schon von vielen genutzt wurden:

  • Ein qualifizierter Namespace hat folgende Struktur \<Vendor Name>\(<Namespace>\)*<Class Name>
  • Jeder Namespace hat einen Haupt-Namespace (Vendor Name)
  • Jeder Namespace kann beliebig viele Unter-Namespaces besitzen
  • Jeder Namespace-Separator wird in einen DIRECTOR_SEPARATOR konvertiert, wenn aus dem Dateisystem geladen wird
  • Das Zeichen “_” (Underscore) wird in einen DIRECTORY_SEPARATOR konvertiert und hat keine spezielle Bedeutung.
  • Der qualifizierte Namespace inkl. Klasse bekommt die Endung “.php” angehängt, um die Datei zu laden.
  • Namespaces, Vendor Names und Klassennamen dürfen alphabetische Zeichen in jeder Kombination aus Groß- und Kleinschreibung enthalten.

Beispiel-Implementation

<?php

namespace WebCodr;

class Loader {

    public static function registerAutoloader() {
        return spl_autoload_register(array ('WebCodr\\Loader', 'load'));
    }

    public static function load($class) {
        if(substr($class, 0, 7) !== 'WebCodr') {
            return;
        }

        $libraryRoot = realpath(__DIR__ . '/../');
        $classFileName = str_replace(array('\\', '_'), DIRECTORY_SEPARATOR, $class) . '.php';
        $fileName = realpath($libraryRoot . DIRECTORY_SEPARATOR . $classFileName);

        if(is_readable($fileName)) {
            include_once($fileName);
        }
    }
}

Die Klasse stellt zwei statische Methoden bereit. Mit Loader::registerAutoloader() wird die Methode Loader::load() als SPL-Autoloader registriert.

Loader::load() prüft zuerst, ob sich die angeforderte Klasse überhaupt in Namespace WebCodr befindet. Falls dies nicht der Fall ist, wird durch den leeren Rückgabewert signalisiert, dass die Klasse mit diesem Autoloader nicht geladen werden kann und die SPL geht zum nächsten registrierten Autoloader über.

Anschließend wird der Pfad zur Klasse zusammengesetzt und die Datei mittels include_once() eingebunden. Optinal könnte man im Fehlerfall natürlich noch eine Exception werfen.

Aufruf des Autoloaders

<?php

include_once('Loader.php');
\WebCodr\Loader::registerAutoloader();

Fazit

PSR-0 ist schnell und einfach implementiert. Für neue Projekte gibt es also keinen Grund, sich nicht daran zu halten. In bestehendem Code könnte es recht aufwendig sein, den Standard umzusetzen – je nach dem, welche Benamungsschemata und Verzeichnisstrukturen bereits verwendet werden.

Man sollte den Aufwand aber nicht scheuen. Was bringt einem schon die tollste Library oder ein cooles Framework, wenn es aufgrund eines schlecht implementierten Autoloaders, kaum eingesetzt werden kann?

IHK != agile

Während der Berufsschule wird einem klassisches Projekt-Management eingetrichtert und immer wieder betont wird, wie wichtig das doch wäre. Dabei wird natürlich nach dem Wasserfallmodell vorgegangen, Gantt-Diagramme gezeichnet (wer braucht bei einer IT-Ausbildung schon Rechner?), mit Netzplänen die Dauer einzelner Schritte in Tagen ausgerechnet (von Hand wohlgemerkt, dementsprechend falsch sind die dann auch meistens) oder die Schüler auf andere Weise mit Dingen malträtiert werden, die zunehmend für die Software-Branche als unbrauchbar betrachtet werden.

Bei Software-Projekten nach dem Wasserfallmodell vorzugehen, hat sich immer wieder als nicht praktikabel herausgestellt. Für Bau-Ingenieure, bei denen sich alles vorher planen und ausrechnen lässt, mag das wunderbar funktionieren.

In der Software-Entwicklung lässt sich aber nun mal nichts vollständig planen. Schon gar nicht zeitliche Angaben, wie sie die IHK für das Projekt auf eine halbe Stunde genau will.

Unvorhergesehene Dinge treten immer auf. Dabei können ganze Lösungsansätze vollkommen eliminiert werden und die Arbeit von Tagen, manchmal sogar Wochen war vergebens.

Oft verursachen selbst keine Fehler große Verzögerungen. Darum versagen klassische Modelle. In solchen Situationen sind sie zu starr und erlauben keine angemessene Reaktion, um das Problem schnell anzugehen. Seit mittlerweile 20 Jahren werden Alternativen entwickelt bzw. setzen sich zunehmend durch.

Agil arbeiten

Als Software-Projekte immer größer bzw. komplexer wurden und damit auch die Probleme durch klassisches Projektmanagement, entstanden die Überlegungen zu agilen Entwicklungsmethoden. Sie sind deutlich flexibler und leichtgewichtiger als klassische Prozesse und widmen sich auch den sozialen Aspekten der Software-Entwicklung.

An dieser Stelle tiefer in das Thema einzusteigen, würde den Rahmen deutlich sprengen, daher verweise ich auf die Blogs meiner Kollegen Dominik Jungowski und Peter Roessler sowie Agile42.

Auch wenn ich erst seit dem Wechsel zu Chip Online mit agiler Entwicklung in Berührung gekommen bin, möchte ich nicht mehr darauf verzichten.

Ich durfte vorher oft genug erleben, wie herkömliche Methoden versagt haben und mir als Programmierer nur Steine in den Weg legten, während agile Methoden Spaß machen und darauf ausgelegt sind, dass ich meine Arbeit angenehmer und besser erledigen kann.

Genau darum verstehe ich auch nicht, warum während der Ausbildung Dinge gelehrt werden, die in jeder größeren längst Software-Schmiede abgeschafft wurden.

Zumal es auch öfter der Fall sein dürfte, dass Auszubildende eigentlich agil in ihrem Betrieb arbeiten, aber in der Berufsschule mit klassischem Projektmanagement konfrontiert werden.

Man sollte noch darauf im Lehrplan eingehen, aber als Negativbeispiel um die Vorteile agiler Entwicklung aufzuzeigen.

Ich wage sogar zu behaupten, dass ein Auszubildender aus unserem hausinternen Workshop zu agilen Methoden innerhalb eines Tages mehr mitnehmen kann, als nach den 60 Tagen Berufsschule des ersten Jahres, in denen Projektmanagement, Wasserfallmodell & Co über sie herfallen.

Übrigens: auch den Lehrern könnte etwas mehr Agilität nicht schaden. Es ließen sich ganz andere Wege beschreiten, die Spaß machen und den Schülern Wissen viel effektiver bzw. dauerhaft vermitteln können … aber das ist ein anderes Thema, über das ich evtl. in einem weiteren Eintrag genauer eingehen werde.

PHP-Tip: Limonade

Wer sich schon mal mit Symfony oder ähnlichen PHP-Frameworks beschäftigt hat, kam sicher schnell zur Erkentnis, dass das die Dinger zwar viel können und generell eine tolle Sache sind, aber hohe Einstiegshürden haben bzw. viel Einarbeitungszeit benötigen, sowie für viele Projekte einfach überdimensioniert sind.

In Ruby hätte man für solche Fälle z.B. Sinatra: übersichtlich, klein, schnell und flexibel. Mit Limonade gibt es so ein Micro-Framework nun endlich für PHP, mit dem sich auch ähnlich elegant entwickeln lässt.

Es reicht eine Datei in ein Script einzubinden, die .htaccess-Datei anzupassen und schon kann man loslegen:

<?php

require_once 'vendors/limonade.php';

// lambda function (>= PHP 5.3)
dispatch('/hello/:name', function() {
    $name = params('name');

    return "Hello, {$name}";
});

run();

Das war es schon. Den ganzen Rest erledigt Limonade und das war noch lange nicht alles, was es kann. Die Routen-Definitionen können beispielsweise auch Wildcards oder reguläre Ausdrücke enthalten. Als Callback lassen sich selbstverständlich auch Objekte bzw. deren Methoden aufrufen (auch statisch) oder man gibt in klassischer Manier einen Funktionsnamen als String an.

Dazu gibt es eine Template Engine mit partiellen Templates, Capture-Möglichkeiten, JSON-Unterstützung, Hooks und Filtern.

Alles davon lässt sich ohne große Einarbeitung sofort nutzen. Natürlich muss man die integrierte Template Engine nicht nutzen und kann auch stattdessen einfach Twig oder Smarty verwenden.