UML-Diagramme aus Text mit PlantUML

Dieser Artikel erschien zuerst auf flagbit.de.

Jeder Entwickler weiß, dass Diagramme oft helfen, über Inhalte zu sprechen. Aber Lust sich in eines der grafischen Tools einzuarbeiten, hat man dann meist doch nicht. Jedes verwendet einen anderen Standard und andere Konventionen, um im Wiki darüber zu reden muss man das Diagramm mühsam als Bild exportieren und eigentlich schreibt man ja am liebsten Code und klickt eher ungern.

Wir haben bei bisher vereinzelt Visual Paradigm UML eingesetzt. An sich ist das Tool ganz brauchbar, vor allem wenn man Java entwickelt und das Round-Trip-Engineering funktioniert. Wir arbeiten aber eher punktuell mit UML-Diagrammen, d.h. meistens möchte man nur eine kleine Änderung machen oder nur ein Fragment der gesamten Software abbilden. Daher lohnt es sich nicht für jeden Entwickler eine Lizenz zu kaufen und wir haben deshalb eine Floating License. Funktioniert auch prima, solange man im Büro (und damit im Firmennetzwerk) ist. Aber sobald man unterwegs oder beim Kunden vor Ort ist kann man nicht “mal eben” das Diagramm anpassen. Außerdem braucht die Software relativ lang zum Starten, was gerade bei kleinen Änderungen auch lästig sein kann (und offen lassen will man das Programm ja auch nicht, weil man dann die Floating License blockiert).

Vor ein paar Wochen bin ich auf der Suche nach einem UML-Plugin für DokuWiki über PlantUML gestolpert. Mit PlantUML lassen sich (mit Hilfe von Graphviz) aus Text UML-Diagramme erstellen. So lässt sich folgendes Authentifzierungs-Sequenzdiagramm einfach mit ein paar Zeilen Code erzeugen: Sequenzdiagramm Authentication

@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
@enduml
Sequenzdiagramm Authentication

Neben Sequenzdiagrammen werden auch Use-Case-Diagramme, Klassendiagramme, Komponentendiagramme, Zustandsdiagramme und Objektdiagramme unterstützt. Außerdem gibt es Integrationen für MediaWiki, Redmine, Confluence, Trac, TinyMCE, Eclipse, NetBeans, Intellij (auch PHPStrom), Word, OpenOffice LaTeX, Doxygen, Sphinx und vieles mehr. Dadurch sind sowohl die Diagramme als auch das Syntax-Wissen portabel und vielseitig einsetzbar. Ich habe als nächstes direkt das Plugin für unser Confluence installiert und auch Sphinx (was wir gerade für Dokumentationen testen) funktioniert prima.

Mime-Type-Problem mit PHP Autoload

Dieser Artikel erschien zuerst auf flagbit.de.

Als ich mir heute mit PHP Autoload (phpab) meine Autoload-Datei neu generieren ließ, war ich bass erstaunt. Statt der üblichen 700 Klassen waren plötzlich nur noch 15 Dateien enthalten.

Nach ein bisschen Debuggen kam ich dann recht schnell dahinter, dass die Ursache in der Klasse \TheSeer\DirectoryScanner\PHPFilterIterator liegt:

<?php
public function accept() {
    $finfo = new \finfo(FILEINFO_MIME);
    return strpos($finfo->file($this->current()->getPathname()), 'text/x-php') === 0;
}

Dort wird nämlich der Mime-Type der Datei überprüft und nur Dateien mit Mime-Type text/x-php werden überhaupt nach Klassen durchsucht. Meine Dateien haben lustigerweise aber zum Großteil text/x-c++ – zumindest wenn ich mir das unter Windows anschaue. In meiner virtuellen Maschine haben die selben Dateien, die dann über vboxsf eingebunden sind, den korrekten Mime-Type.

Klar – ich kann das erstmal umgehen, indem im PHPFilterIterator ein return true einbaue; aber schön ist anders. Hat jemand einen Tipp wie man den Mime-Type einer Datei ändern kann oder noch interessanter: wie das Problem überhaupt entstanden sein könnte? Da es in der Linux-VM korrekt angezeigt wird ist ja vermutlich irgendwas an meinem Windows kaputtkonfiguriert…

ISO-8601-Stolperfalle bei Zend_Date

Jeder der mit Datumswerten gearbeitet hat, weiß (oder sollte zumindest) um die kleinen und großen Untiefen wie Zeitzonen, Zeitumstellung und Schaltjahre die es dabei zu umschiffen gilt. Neben der seit PHP 5.2 verfügbaren DateTime-Klasse gibt es im Zend Framework die ältere Zend_Date-Komponente. Mit beiden lässt sich die Handhabung von Datumswerten in einer Applikation vereinfachen und vereinheitlichen:

<?php
$date = new Zend_Date('01.01.2011', null, 'de_DE');
echo $date; // 01.01.2011 00:00:00

Soweit so gut. Formatieren kann man das Datum mit Zend_Date auch, zum Beispiel zur Verwendung in einem MySQL-Query:

<?php
$date = new Zend_Date('01.01.2011', null, 'de_DE');
echo $date->toString('yyyy-MM-dd HH:mm:ss'); // 2011-01-01 00:00:00

Bei der Verwendung der Formatcodes ist darauf zu achten “yyyy” und das ähnlich aussehende “YYYY” keinesfalls zu verwechseln! Beide geben zwar das Jahr aus, aber mit einem kleinen, aber gewichtigen Unterschied.

Intern verwendet Zend_Date zur Formatierung die date-Funktion und konvertiert die Zend_Date-Formatcodes entsprechend. Während “yyyy” zu “Y” konvertiert wird und das erwartete Ergebnis liefert, wird “YYYY” zu “o” umgewandelt. Und “o” hat folgende Bedeutung:

ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
php.net

Da die erste Tage im Jahr 2011 noch zur letzten Kalenderwoche des Jahres 2010 gehören, wird gemäß ISO 8601 das Jahr 2010 ausgegeben. Und das dürfte in den seltesten Fällen das sein, was man möchte…

PHP 5.4: Die wichtigsten Änderungen

Dieser Artikel erschien zuerst auf flagbit.de.

Vor einigen Tagen war es soweit: PHP 5.4 wurde veröffentlicht. Die Anzahl der Änderungen sind, im Vergleich mit PHP 5.3, überschaubar, aber dennoch sollte man sie kennen. Die wichtigsten Änderungen kurz zusammengefasst:

PHP 5.4 - So what?! from flagbit

Einige Code-Beispiele zu den Änderungen sind im zugehörigen Git-Repository: https://bitbucket.org/flagbit/php54.

Die Informationen zu Traits und dem Diamond-Problem sind im seperaten PDF.

Vorsicht bei der Verwendung von isset() mit Arrays

Manchmal sucht man Fehler, die man wieder und wieder überliest, weil man die falschen Annahmen getroffen hat und der Code oberflächlich richtig aussieht. Erfahrungsgemäß gibt es dann ein paar “übliche Verdächtige”, die man sich genauer anschaut. Zuweisungen im if-Statement, Vergleiche ohne Typprüfung, empty() und isset() sind dabei immer heiße Kandidaten.

Wenn man die Handbuch-Seite zu isset() durchliest, klingt es, als wäre es problemlos möglich auch die Existenz von Array-Keys zu prüfen. Sogar ein schönes Beispiel gibt es dazu:

<?php
$a = array (
    'test' => 1,
    'hello' => NULL,
    'pie' => array('a' => 'apple'),
);
 
var_dump(isset($a['test']));            // TRUE
var_dump(isset($a['foo']));             // FALSE
var_dump(isset($a['hello']));           // FALSE
 
// The key 'hello' equals NULL so is considered unset
// If you want to check for NULL key values then try: 
var_dump(array_key_exists('hello', $a)); // TRUE
 
// Checking deeper array values
var_dump(isset($a['pie']['a']));        // TRUE
var_dump(isset($a['pie']['b']));        // FALSE
var_dump(isset($a['cake']['a']['b']));  // FALSE

Doch ganz so einfach ist es leider nicht. Interessant ist hier vor allem die letzte Zeile. Erst auf den zweiten Blick fällt auf, dass als erster Schlüssel “cake” statt “pie” verwendet wird. Und das ist der einzige Grund dafür, dass FALSE zurückgegeben wird. Ändert man nämlich den ersten Schlüssel auf “pie”, wird TRUE zurückgegeben, auch wenn der dritte Key nicht existiert:

<?php
$a = array(
    'test' => 1,
    'hello' => NULL,
    'pie' => array('a' => 'apple'),
);
 
var_dump(isset($a['pie']['a']['b']));  // TRUE

Das Problem und die Lösung wird auch in einem Kommentar von 2004 angesprochen. Die Prüfung mit isset() reicht nicht aus. Zusätzlich muss auf den vorletzen Key mit is_array() geprüft werden:

<?php
$a = array (
    'test' => 1,
    'hello' => NULL,
    'pie' => array('a' => 'apple'),
);
 
var_dump(isset($a['pie']['a']['b']) && is_array($a['pie']['a']));  // FALSE

Wichtig ist hier auch, dass man zuerst das isset() und danach das is_array() schreibt, sonst bekommt man einen Undefined-index-Fehler, wenn der Schlüssel komplett nicht vorhanden ist. Ein alleinstehendes isset() in Verbindung mit einem Array sollte einen also immer misstrauisch machen. Je nach Fall sollte ein is_array() ergänzt oder ein array_key_exists() bevorzugt werden.

Grep für Application-Logs

Bei der Fehlersuche bieten einem die Application-Logs oftmals einen guten Einstiegspunkt. Doch die Suche darin kann zum Teil recht mühsam sein. Vor allem wenn man nicht nach der Fehlermeldung selbst suchen will, sondern zum Beispiel alle Fehler haben will, die einen bestimmten Methodenaufruf im Stacktrace haben. Und dann wollte ich auch nicht nur die Zeile haben, sondern den ganzen Log-Eintrag, inklusive Message und komplettem Stacktrace.

Ich habe mir dafür ein kleines PHP-Skript geschrieben. Vielleicht findet es ja sonst jemand nützlich:

<?php
// loggrep.php 2011-12-08
if (2 !== $argc && 3 !== $argc) {
    echo "USAGE: {$argv[0]} <pcre-pattern> <file>\n";
    exit(1);
}
 
$filename = 'php://stdin';
if (isset($argv[2])) {
    $filename = $argv[2];
}
$fp = fopen($filename, 'r');
 
// "2011-11-22T02:16:34+01:00 - "
$startPattern = '#^\d{4}(-\d{2}){2}T\d{2}(:\d{2}){2}\+\d{2}:\d{2} - #';
$searchPattern = $argv[1];
 
$found = false;
 
while ($line = fgets($fp)) {
    if (preg_match($startPattern, $line)) {
        if (true === $found) {
            foreach ($buffer as $bufferedLine) {
                echo $bufferedLine;
            }
        }
 
        $buffer = array();
        $found = false;
    }
 
    if (preg_match($searchPattern, $line)) {
        $found = true;
    }
 
    $buffer[] = $line;
}

Der Aufruf sieht dann so aus:

$ php loggrep "#Mage_Core_Block_Template#" exception.log

Wichtig ist, dass der Suchbegriff mit PCRE-Syntax (inklusive Delimiter) angegeben wird.

Update 08.11.2011

Jetzt neu: Falls keine Datei angegeben wird, kann das Skript auch von STDIN lesen. Damit kann man auch einfach in gepackten Dateien suchen:

$ zcat exception.log.1.gz | php loggrep "#Mage_Core_Block_Template#"

Zend_Cache Backends

Dieser Artikel erschien zuerst auf flagbit.de.

Hier war in den letzten Monaten mal wieder Beitrags-Flaute, was vor allem daran liegt, dass ich seit etwa vier Monaten an einem Zend-Framework-Projekt entwickle und nur noch sporadisch mit Magento zu tun habe. Deshalb geht es auch heute nicht um Magento direkt, sondern um die Cache-Komponente von Zend Framework. Magento-Entwickler müssen jetz nicht gleich aufhören zu lesen, denn Zend_Cache bildet auch die Basis für den Cache in Magento.

Read More

Verkürzte URLs auflösen

Die verkürzten URLs von tinyurl und Konsorten mögen ja ganz nett sein, wenn man twittern will, aber ansonsten gleicht das meine Meinung nach eher einem Minenfeld: keiner weiß so genau was einen da erwartet.

Genau dieses Problem geht das Firefox-Addon Long URL Please an. Damit werden die verkürzten URLs aufgelöst und in der Originalform angezeigt.

Wer übrigens behauptet, das wäre auch praktisch um das Layout bei langen Links zu erhalten verwendet nur schlechte Software. MusicBrainz! verkürzt den angezeigten Link nämlich einfach, was der Funktion und Transparenz keinen Abbruch tut, da der vollständige Link ja immernoch vor'm draufklicken in der Statusleiste angezeigt wird.

Verlauf für neue Tabs

Mit ging es schon oft so, dass ich einen Artikel oder ähnliches gelesen habe und dabei die interessanten Links in neuen Tabs im Hintergrund geöffnet habe. Am Ende des Artikels schließe ich dann den ursprünglichen Tab und lese in den neuen Tabs weiter. Wenn ich dann nochmal zurück will, geht das normalerweise nur über den "normalen" Verlauf und nicht über die Zurück-Taste im Browserfenster, weil die Tabs ja neu sind und somit keinen Verlauf haben.

Genau dieses Verhalten wird durch die Firefox-Erweiterung Tab History geändert. Neue Tags "erben" damit den Verlauf des öffnenden Tabs und somit lässt sich auch in den neuen Tabs Vergangenes einfach über den Zurück-Knopf zurückholen.

PDF-Seiten in LaTeX einbinden

An meiner Hochschule ist für die Abschlussarbeit ein festes Titelblatt vorgegeben. Jetzt könnte man mit einem PDF-Merge-Programm die generierte PDF-Thesis mit dem Titelblatt mergen, aber dabei gehen leider die Links im PDF-Dokument kaputt (und zeigen eine Seite daneben). Viel eleganter ist das Einbinden des Titelblatts direkt in LaTeX: möglich wird das durch das Paket pdfpages

\documentclass{...}
% ...
\usepackage{pdfpages}
% ...
\begin{document}
\includepdf{deckblatt.pdf}
% ...
\end{document}