Technik, Gothic und Anderes

Technik ist Spiel, Gothic ist ernst und Zeit hat man zuviel

Archiv für 'Theorie und Schnipsel' Kategorie


Eine eigene Programmiersprache erschaffen? Lexer und Parser in PHP!

Geschrieben von skaldrom am 25. October 2007

Ich will Gott sein!

Paper SnippetsWer träumt nicht davon: Eine eigene Programmiersprache, denn der Syntax und das Wort sind Ausdruck! Einen Schritt weiter gehen und vom Programmiersprachenanwender zum Programmiersprachenerschaffer aufsteigen! Auch dies ist nun möglich in PHP, doch leider nur mit minimaler Dokumentation.

Ich gehe davon aus, dass die Leserin/Der Leser Grundlagen im Compilerbau besitzt. Ansonsten müsste man sich diese noch aneignen. Compilerbau ist rekursiv und somit göttlich! *schwärm*.

Das Beispiel

Ich möchte einen Interpreter bauen für normale, arithmetische Ausdrücke wie 9*(8+6)-2/(-4). Klar könnte man diesen einfach durch eval hauen, aber erstens macht das nicht so viel Spass und zweitens ist dieses Beispiel sowas wie das Hello World des Compilerbaus.

Als BNF habe ich folgendes vor:

<Expression> ::= '+' <Expression> // Unäres + (Vorzeichen)
<Expression> ::= '-' <Expression> // Unäres - (Vorzeichen)
<Expression> ::= <Term>
<Expression> ::= <Expression> '+' <Term>
<Expression> ::= <Expression> '-' <Term>
<Term> ::=  <Factor>
<Term> ::= <Term> '*' <Factor>
<Term> ::= <Term> '/' <Factor>
<Factor> ::=  NUMBER // Ok, hier hab ich abgekürzt ;) Soll der Lexer erledigen...
<Factor> ::= '(' <Expression> ')'

Ein Problem stellen die unären Plus- und Minusoperatoren dar. Sie besitzen das gleiche Symbol, nicht aber die gleiche Priorität wie ihre binären Kollegen. Lemon erlaubt keine änderung der Priorität nach Regeln und so funzen jetzt halt auch Konstrukte wie —+++—+1…

Die Werkzeuge

Ich verwende PHP_LexerGenerator, der kompatibel zum re2c Format ist und den PHP_ParserGenerator der in grossen Teilen dem LEMON Parser Generator entspricht. Heruntergeladen werden können sie mittels folgenden Zeilen, die allerdings unter Umständen noch eine Anpassungen der Versionsnummer brauchen:

pear install channel://pear.php.net/PHP_ParserGenerator-0.1.5
pear install channel://pear.php.net/PHP_LexerGenerator-0.3.4

Als einziges, weiteres Beispiel für diese Libs ausserhalb der Doku habe ich nur dieses Drupalmodul gefunden.

Die Realisierung

Der Lexer

Für den Lexer muss eine plex Datei erstellt werden. Sie beinhaltet den Rumpf des Lexerobjekts und die lexikalischen Regeln:

TermLexer.plex

<?php
class TermLexer
{
    private $data;
    public $counter;
    public $token;
    public $value;
    public $node;
    public $line;
    private $state = 1;

    function __construct($data)
    {
        $this->data = $data;
        $this->counter = 0;
        $this->line = 1;
    }

/*!lex2php
%input $this->data
%counter $this->counter
%token $this->token
%value $this->value
%line $this->line
number = /[0-9]+(\.[0-9]+)?/
multiplication = /\*/

division = @/@
plus = /\+/
minus = /\-/
openP = /\(/
closeP = /\)/
other = /./
*/
/*!lex2php
%statename START
multiplication {
  $this->token = TermParser::TP_MULTIPLICATION;
  //echo "multiplication: ".$this->value."\n";
}
division {
  $this->token = TermParser::TP_DIVISION;
  //echo "division: ".$this->value."\n";
}
plus{
  $this->token = TermParser::TP_PLUS;
  //echo "plus: ".$this->value."\n";
}
minus{
  $this->token = TermParser::TP_MINUS;
  //echo "plus: ".$this->value."\n";
}
openP {
  $this->token = TermParser::TP_OPENP;
  //echo "openP: ".$this->value."\n";
}
closeP {
  $this->token = TermParser::TP_CLOSEP;
  //echo "closeP: ".$this->value."\n";
}
number {
  $this->token = TermParser::TP_NUMBER;
  //echo "number: ".$this->value."\n";
}
other {
  return false;
}
*/

}

So richtig laufen tuts aber noch nicht, da er Konstanten des Parsers benötigt. Die stehen erst zur Verfügung wenn wir den Parser auch gebaut haben.

Der Parser

Der Parser wird vom Lexer mit Tokens gefüttert. Die Notation im Lemon-Style unterscheidet sich von Lex und Yacc, ist aber durchaus logisch. Hier der ganze Code, mal hingeknallt:

TermParser.y

/* This is an example for a Parser in PHP */
%name TP_
%declare_class {class TermParser}
%include_class
{
    // states whether the parse was successful or not
    public $successful = true;
    public $retvalue = 0;
    private $lex;
    private $internalError = false;

    function __construct($lex) {
        $this->lex = $lex;
    }
}

%token_prefix TP_

%parse_accept
{
    $this->successful = !$this->internalError;
    $this->internalError = false;
    $this->retvalue = $this->_retvalue;
    echo "WORKED!!\n\n";
}

%syntax_error
{
    $this->internalError = true;
    echo "Syntax Error on line " . $this->lex->line . ": token '" .
        $this->lex->value . "' count ".$this->lex->counter." while parsing rule: ";
    foreach ($this->yystack as $entry) {
        echo $this->tokenName($entry->major) . '->';
    }
    foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
        $expect[] = self::$yyTokenName[$token];
    }
    echo "\n"
    throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN. '), expected one of: ' . implode(',', $expect));
}

%left PLUS MINUS.
%left MULTIPLICATION DIVISION.

start(res)       ::= expression(expr). { res = expr; }

/* Unary minus or plus */
expression(res)  ::= PLUS expression(e). { res = +e; }
expression(res)  ::= MINUS expression(e). { res = -e; }

/* The common stuff */
expression(res)  ::= term(t). { res = t; }
expression(res)  ::= expression(e1) PLUS term(t2). { res = e1+t2; }
expression(res)  ::= expression(e1) MINUS term(t2). { res = e1-t2; }

term(res)        ::= factor(f). { res = f; }
term(res)        ::= term(t1) MULTIPLICATION factor(f2). { res = t1*f2; }
term(res)        ::= term(t1) DIVISION factor(f2). { res = t1/f2; }

factor(res)      ::= NUMBER(n). { res = n; }
factor(res)      ::= OPENP expression(e) CLOSEP. { res = e; }

Generieren der eigentlichen Parser und Lexer

Damit Parser und Lexer generiert werden und Terme gefüttert werden können, sei hier ein kleines Beispiel gegeben:

<?php
// Create Parser
passthru('php /usr/share/php/PHP/ParserGenerator/cli.php TermParser.y');

// Create Lexer
require_once 'PHP/LexerGenerator.php';
$lex = new PHP_LexerGenerator('TermLexer.plex');

# Test
include_once("TermParser.php");
include_once("TermLexer.php");

$teststr='9*(8+6)-2/(-4)';

$lex = new TermLexer($teststr);
// $lex = new TermLexer(file_get_contents($lexerfile));

echo "-------------------------------\n";
echo " Parsing $teststr\n";
echo "-------------------------------\n";
$parser = new TermParser($lex);
while ($lex->yylex())  {
    echo "Parsing  {$lex->token} Token {$lex->value} \n";
    $parser->doParse($lex->token, $lex->value);
}
$parser->doParse(0, 0);

print "Returnvalue: ".$parser->retvalue."\n";
?>

Und nu?

Nun viel Spass… Ich bin auf der Suche nach der EBNF für RegExps, wenn die jemand hat :) …. Ihr könnt den ganzen Code hier auch downloaden…

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | 7 Komentare »

Moodle Block Resource-Download

Geschrieben von skaldrom am 22. October 2007

Der Moodle Resource Download Block

Moodle Resource Download Block Auf vielfachen Schülerwunsch hin habe ich einen Block für unser Learning Management System Moodle codiert: Den Resource Download Block. Er erlaubt den Download aller Kursdateien und Verzeichnisse in einem ZIP-Archiv. Dieses Zip-Archiv wird wie im Beitrag Zipdateien on-the-fly erstellen mit PHP dynamisch erstellt, da (in der Theorie) jeder Lernende eine andere Kursansicht haben kann.

Einen Block erstellen ist relativ einfach, wenn man dem Block Howto folgt, aber der Teufel liegt wie immer im Detail.

Configwerte

Jeder Block kann verschiedene Konfigurationswerte erfragen und erhalten. Diese unterscheiden sich aber, ob der Block global (pinned, sticky) ist, in welchem er nur von einem Ort aus konfiguruiert wird oder ob er den individuellen Kursen hinzugefügt wurde, wobei dann jede Instanz ihre eigene Konfiguration hat. Ich habe mich für den zweiten Weg entschieden.

Um die Konfigurationsvariablen mit einem Defaultwert zu versehen, muss man sich in der instance_config_print() Methode darum kümmern:

if (!isset ($this->config)) {
    // ... teacher has not yet configured the block, let's put some default values here to explain things
    $this->config->exclusionregexp= block_downloader_default_exclusionregexp();
    $this->config->compression= block_downloader_default_compression();
    $this->config->maxsize= block_downloader_default_maxsize();
}

Die Datei, die das Zip zusammenstellt ist mehr oder weniger ausserhalb von Moodle, da sie nicht “als Block” erscheinen kann. Um da an die Konfigurationsdaten zu kommen, muss man etwas mehr Aufwand treiben. Sie befinden sich base64 codiert in den Tabellen block_instance respektive blocks_pinned für globale Blocks. courseid und instanceid werden dabei vom Link im Block übergeben.

<?php

/**
 * Create the zip on the fly and push it to the browser.
 */


require_once ('../../config.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/archive.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/downloadlib.php');
require_once ($CFG->dirroot . '/lib/filelib.php');
require_once ($CFG->dirroot . '/lib/moodlelib.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/downloadlib.php');

$courseid= required_param('courseid', PARAM_INT); // Course identification
$instanceid= required_param('instanceid', PARAM_INT); // Instance of the block

// Securitycheck
[SNIP]

// Get Config Data
$where= "pagetype = 'course-view' AND visible = 1 AND id=" . addslashes($instanceid);

// Instance?
$blockarr= get_records_select('block_instance', $where . " AND pageid=" . addslashes($courseid));

// Maybe pinned? In this case we have no courseid
if (!$blockarr) {
    $blockarr= get_records_select('block_pinned', $where);
}

if (!$blockarr) {
    error("cannot find block with: " . $where . " AND pageid=" . addslashes($courseid));
    exit ();
}

// Take the first result
$block= array_pop($blockarr);

$configdata= unserialize(base64_decode($block->configdata));

print_r($configdata);

[...]

Dateien pro Kurs abholen

Das Zusammenstellen aller Pfade für Dateien, die ein Benutzer sieht und auf die er Zugriff hat ist echt mühsam. Ich wünsche mir hier ein Bisschen den Servicegedanken: Beispielsweise würde man viel Zeit sparen, wenn der Entwickler mit dem entsprechenden Wissen Funktionen wie can_read($userid, $resourceid) implementieren würde. Zusätzlich wäre man unabhängig vom verwendeten Absicherungsschema. Ich werde das zukünftig in meinen Programmen berücksichtigen.

Reaktionen

Die Community hat - wie meistens bei Moodle - sehr nett auf die Veröffentlichung reagiert. Zwei Tage nach Version 1.0.0 habe ich schon eine Slovakische Übersetzung und diverse Hinweise auf Bugs erhalten.

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel, Web | Keine Kommentare »

Zipdateien on-the-fly erstellen mit PHP

Geschrieben von skaldrom am 17. October 2007

Das Problem

On-The-Fly An unserer Schule wird Moodle als LMS (Learning Management System) verwendet. Es eignet sich sehr gut um Unterrichtsunterlagen wie Skripts, Aufträge und Präsentationen zu publizieren und den Lernenden auf eine angenehme Art und Weise zur Verfügung zu stellen. In einigen Kursen gibt es sehr viele dieser Ressourcen (Dateien und Verzeichnisse) zum Download und alleine schon das Anklicken und Speichern nimmt einen grossen Teil des zur Verfügung stehenden Zeitbudgets ein.

Einzelne Ressourcen können von der Lehrperson unsichtbar gemacht werden und nicht alle Dateien eines Kurses stehen zum Download bereit. Die Ansicht ist also sehr individuell und kann sich schnell ändern. Die Dateien werden über eine Versionsverwaltung in das System eingespiesen und können sich so ausserhalb des Systems verändern. Leider verunmöglicht dies die offensichtliche Lösung, einfach ein statisches Zip-Archiv zum Download zur Verfügung zu stellen.

Screenshot Moodle

Die Lösung

Das Zip muss also dynamisch (im Speicher) generiert werden bei einem Aufruf. Fündig wurde ich bei der Klasse archive bei phpclasses.org. Neben Zip-Archiven kann sie auch tar’s, gzip’s und bzip2’s erzeugen. Ich habe einige, kleine Erweiterungen vorgenommen, die noch beschrieben werden.

Die Handhabung ist sehr einfach:

// Archivklasse einbinden:
require_once ('lib/archive.php');

// Objekt erzeugen. Das Argument bezeichnet den Dateinamen
$zipfile= new zip_file('Meine Zipdatei.zip');

// Die Optionen
$zipfile->set_options(array (
        'basedir' => "/home/me/toZip/", // Das Basisverzeichnis. Sonst wird der ganze Pfad von / an im Zip gespeichert.
        'followlinks' => 1, // Symlinks sollen berücksichtigt werden
        'inmemory' => 1, // Die Datei nur im Speicher erstellen
        'level' => 6, // Level 1 = schnell, Level 9 = gut
        'recurse' => 1, // In Unterverzeichnisse wechseln
        // Wenn zu grosse dateien verarbeitet werden, kannes zu einem php memory error kommen
        // Man sollte nicht über das halbe memory_limit (php.ini) hinausgehen
        'maxsize' => 12*1024*1024 // Nur Dateien die <= 12 MB gross sind zippen
));

// Alle Dateien im Verzeichnis /home/me/toZip/Stuff hinzufügen
// Alle ".doc" Dateien und alle Ordner im Verzeichnis /home/me/toZip/Letters hinzufügen
$zipfile->add_files(array("Stuff", "Letters/*.doc"));

// Alle ".tmp" dateien in Stuff ausschliessen
$zipfile->exclude_files("htdocs/*.tmp");

// Alle Dateien in ".svn" und "CVS" Verzeichnissen ausschliessen (Regular Expressions)
$zipfile->exclude_regexp_files('.*/CVS|.*/CVS/.*|.*/\.svn|.*/\.svn.*');

// Archiv erstellen
$zipfile->create_archive();

// Archiv zum Download anbieten
$zipfile->download_file();

Änderungen

Ich habe folgende Dinge gegenüber der original archive.php geändert:

  • Mittels exclude_regexp_files() können Dateien auf Grund von Regular Expressions ausgeschlossen werden.
  • Die maxsize Option erlaubt das Ausschliessen von Dateien die grösser sind als die angegebene Zahl in Byte.
  • Verzeichnisse unter Linux werden als Verzeichnisse behandelt (Bug?)
  • exclude_file() prüft, ob eine Datei ausgeschlossen würde

Meine Version kann natürlich auch frei heruntergeladen werden.

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | 9 Komentare »

Schere, Stein, Papier: Bewertungen im Kreis

Geschrieben von skaldrom am 27. September 2007

Round n RoundDie Regeln von Schere, Stein, Papier sind weitherum bekannt. Zwei Mitspieler zeigen gleichzeitig ein Symbol, danach wird ausgewertet: Schere wird von Stein geschlagen → Stein wird von Papier geschlagen → Papier wird von Schere geschlagen. Die Gewinne sind im Kreis herum (zirkulär wie der geneigte Klugscheisser sagen würde). Diese Kreisbewertung gibt es oft, auch wenn mir jetzt überhaupt kein Beispiel dazu einfällt :-D .

Nehmen wir an, wir müssten die Auswertung programmieren und bestimmen, wer gewonnen hat. Die Symbole seien Konstanten und auf Ziffern gemappt:

define("SCHERE", 0);
define("STEIN", 1);
define("PAPIER", 2);

Insgesamt gibt es 9 Fälle (ich kann 3 Symbole zeigen, mein Mitspieler kann auf jedes mit 3 anderen Symbolen reagieren: 3 mal 3 macht 9). Der Programmierer, der nach SLOCs bezahlt wird würde also 9 ifs schreiben.

Mein Ziel ist es aber, nur 3 ifs zu benötigen für die 3 Resultate: Spieler 0 gewinnt, Spieler 1 gewinnt und unentschieden.

Die erste Reduktion ist trivial: Wenn beide dasselbe Symbol zeigen, dann ist unentschieden. Damit sind wir auf 7 ifs.

Weiter hilft eine Faustregel des alten Informatikers: Wenn etwas im Kreis herum geht, ist fast immer ein Modulo beteiligt.

Die Grundüberlegung: Wenn der Spieler 0 um 1 darunter liegt (beispielsweise STEIN=1 unter PAPIER=2) dann hat Spieler 1 gewonnen. Und nun muss das ganze halt noch im Kreis herum: PAPIER liegt 1 unter SCHERE.

Wenn nicht unentschieden ist, und auch Spieler 1 nicht gewonnen hat, ja dann, hmm, hat wohl Spieler 0 gewonnen!

Code!

define("SCHERE", 0);
define("STEIN", 1);
define("PAPIER", 2);

[...]
    if($symbolFromPlayer0==$symbolFromPlayer1) {
        // Unentschieden:
    } elseif( ($symbolFromPlayer0+1)%3 == $symbolFromPlayer1) {
        // Player1 hat gewonnen
    } else {
        // Player0 hat gewonnen
    }
[...]

Jou, juhu, hat geklappt! Wir haben unsere 3 ifs…

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | Keine Kommentare »

Observer Pattern in PHP mit der SPL

Geschrieben von skaldrom am 12. September 2007

Das Observer Pattern

Observed!Normalerweise läuft es mir kalt den Rücken runter wenn ich das Wort Pattern höre. Ich will keine Fassaden, Dekoratöre und Factories in meinen Applikationen (ausser für Anwendungen in der entsprechenden Problemdomäne). An Patterns lastet der Geruch von CRC-Karten, Pair-Programming, millionenzeiliger, selbstgeschriebener Frameworks die Dinge vereinfachen sollen die man nicht begriffen hat und anderen Dingen an, die ich nicht so mag :-) .
Eine Ausnahme ist das Observer-Pattern (was hat es wohl angestellt, dass es auch Pattern geschimpft wird?). Grundsätzlich geht es darum, dass eine Klasse für Daten oder Ereignisse zuständig ist (das Subject). Andere Klassen können sich als Beobachter (Observer) anmelden und werden bei Statusänderungen informiert. MVC (Model-View-Controller) macht rege davon Gebrauch, indem das Model die Daten verwaltet (Subject) und Views (Observer) die Daten darstellen. Eine View könnte beispielsweise für eine Baumansicht verantwortlich sein, während sich eine andere View um eine listenartige Ansicht kümmert. Ändern sich nun die Daten, so werden beide benachrichtigt und können ihre Ausgaben anpassen. So ist es einfach, neue Klassen hinzuzufügen die auf Statusänderungen reagieren können müssen.

Das Observerpattern mit SPL

SPL ist die Standard PHP Library die ab PHP 5.0 dabei ist, stetig weiterentwickelt wird und ohne zusätzlichen Aufwand/Code verwendet werden kann. Sie erlaubt ein paar ganz nette Gags, aber das soll hier nicht Thema sein.

Ohne SplObjectStorage

Leider sind Beispiele für die PHP Implementierung etwas knapp gesät. Das PHP-Wiki ist offline und auch sonst gibt es neben PHP Avancado nur wenig Code. Die offizielle SPL-Seite hat zwar viel Doku aber leider auch keine Beispielimplementierung.

Darum hier etwas Code. Die Klasse Ticker generiert jede Sekunde einen Tick. Einige Observer beobachten sie und reagieren darauf.

Die erste Version arbeitet ohne SplObjectStorage, damit das Prinzip klar wird. Man muss sich somit selber um die Speicherung der Observer kümmern.

<?php
/**
 * Observer pattern without SplObjectStorage
 *
 * Little example because there are not much of them on the net.
 * It implements a Model-View relationship.
 * Taken from <a href="http://blog.oncode.info">Technik, Gothic und Anderes</a>.
 * Needs at least PHP 5.1
 * 
 * @see http://www.php.net/~helly/php/ext/spl/interfaceSplObserver.html
 * @see http://phpavancado.net/comment/reply/410
 * @see http://wiki.cc/php/?title=SplObserver
 * @author Skaldrom Y. Sarg
 */


/**
 * The Class we are observing
 *
 * This class generates a tick every second.
 */

class Ticker implements SplSubject {
    protected $numTicks= 0; // Stores the ticks
    protected $observers= array (); // Stores all observers

    /**
     * Needed to register observers
     *
     * Note the typehinting here.
     */

    public function attach(SplObserver $observer) {
        $this->observers[]= $observer;
    }

    /**
     * Needed to unregister observers
     *
     * Note the typehinting here.
     */

    public function detach(SplObserver $observer) {
        // Not implemented here
    }

    /**
     * Notify all Observers that we have changed
     */

    public function notify() {
        foreach ($this->observers as $obj) {
            $obj->update($this);
        }
    }

    /**
      * Get the number of ticks
      */

    public function getNumTicks() {
        return $this->numTicks;
    }

    /**
     * The ticker itslef
     */

    public function doTicks() {
        while ($this->numTicks < 10) {
            sleep(1); // Sleep for one second
            $this->numTicks++;
            echo "\nTicker: Added a Tick (" . $this->numTicks . ")\n";
            $this->notify(); // Tell all observers we have changed     
        }
    }
}

/**
 * Observer that counts the ticks
 */

class Observer1 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 1: Wow, a Tick!!! We are at tick number " . $subject->getNumTicks() . "\n";
    }
}

/**
 * Observer that calculates the number of ticks until 10
 */

class Observer2 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 2: TickTick, there are " . (10 - $subject->getNumTicks()) . " ticks left until 10\n";
    }
}

$ticker= new Ticker(); // The subject to be observed

$observer1_1= new Observer1(); // First observer of type 1
$ticker->attach($observer1_1);

$observer1_2= new Observer1(); // Another observer of type 1
$ticker->attach($observer1_2);

$observer2= new Observer2(); // Observer of type 2
$ticker->attach($observer2);

// Start the run
$ticker->doTicks();
?>

Und die Ausgabe (im Sekundentakt):

Ticker: Added a Tick (1)
Observer 1: Wow, a Tick!!! We are at tick number 1
Observer 1: Wow, a Tick!!! We are at tick number 1
Observer 2: TickTick, there are 9 ticks left until 10

Ticker: Added a Tick (2)
Observer 1: Wow, a Tick!!! We are at tick number 2
Observer 1: Wow, a Tick!!! We are at tick number 2
Observer 2: TickTick, there are 8 ticks left until 10
[...]

SplObjectStorage

SplObjectStorage nimmt uns einiges ab. Er implementiert aber das Interface SplSubject nicht. SplObjectStorage hat eine Implementierung von attach() und detach() und SplSubject auch, aber eine andere :-( . Nun muss man halt zusammenleimen, sonst wärs noch kürzer…

<?php
/**
 * Observer pattern with SplObjectStorage
 *
 * Little example because there are not much of them on the net.
 * It implements a Model-View relationship with the help of the SPL object storage class.
 * Taken from <a href="http://blog.oncode.info">Technik, Gothic und Anderes</a>.
 * Needs at least PHP 5.1
 * 
 * @see http://www.php.net/~helly/php/ext/spl/classSplObjectStorage.html
 * @see http://wiki.cc/php/?title=SplObjectStorage
 * @author Skaldrom Y. Sarg
 */


/**
 * The Class we are observing
 *
 * This class generates a tick every second.
 */

class Ticker extends SplObjectStorage implements SplSubject {
    protected $numTicks= 0; // Stores the ticks

    /**
     * Needed to register observers
     *   
     * This method glues plObjectStorage::attach() and SplSubject::attach(). Otherwise you will get something like:
     *  "Fatal error: Declaration of SplObjectStorage::attach() must be compatible with that of SplSubject::attach()"
     */

    public function attach(SplObserver $observer) {
        // Duplicate-Checking is done in SplObjectStorage::attach()
        parent :: attach($observer);
    }

    /**
     * Needed to unregister observers
     *   
     * This method glues plObjectStorage::detach() and SplSubject::detach(). Otherwise you will get something like:
     *  "Fatal error: Declaration of SplObjectStorage::detach() must be compatible with that of SplSubject::dettach()"
     */

    public function detach(SplObserver $observer) {
        // Duplicate-Checking is done in SplObjectStorage::attach()
        parent :: detach($observer);
    }

    /**
     * Notify all our observers
     *
     * The observers are stored in the SplObjectStorage and this class implements an iterator.
     */

    public function notify() {
        foreach ($this as $observers) {
            $observers->update($this);
        }
    }

    /**
      * Get the number of ticks
      */

    public function getNumTicks() {
        return $this->numTicks;
    }

    /**
     * The ticker itslef
     */

    public function doTicks() {
        while ($this->numTicks < 10) {
            sleep(1); // Sleep for one second
            $this->numTicks++;
            echo "\nTicker: Added a Tick (" . $this->numTicks . ")\n";
            $this->notify(); // Tell all observers we have changed     
        }
    }
}

/**
 * Observer that counts the ticks
 */

class Observer1 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 1: Wow, a Tick!!! We are at tick number " . $subject->getNumTicks() . "\n";
    }
}

/**
 * Observer that calculates the number of ticks until 10
 */

class Observer2 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 2: TickTick, there are " . (10 - $subject->getNumTicks()) . " ticks left until 10\n";
    }
}

$ticker= new Ticker(); // The subject to be observed

$observer1_1= new Observer1(); // First observer of type 1
$ticker->attach($observer1_1);

$observer1_2= new Observer1(); // Another observer of type 1
$ticker->attach($observer1_2);

$observer2= new Observer2(); // Observer of type 2
$ticker->attach($observer2);

// Start the run
$ticker->doTicks();
?>

Fazit

Man beachte: Hier könnten noch viele beliebige Observer hinzugefügt werden, ohne dass das Subject (Ticker) verändert werden muss.

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Theorie und Schnipsel | Keine Kommentare »

Structorizer: Struktogramme unter Windows und Linux (Nachschlag)

Geschrieben von skaldrom am 8. September 2007

Die Freuden des Bloggens

Blogging ist genial. Im Beitrag Struktogramme (Nassi Shneiderman) mit dem Computer zeichnen habe ich leider Structorizer nicht zum Laufen gebracht. Dem Autor zu schreiben traute ich mich nicht ganz, da ich sicher war, dass es nur an meiner eigenen, angebohrenen Blödheit gelegen hat. Vielleicht stimmt das auch, aber Bob Fisch hat höchtpersönlich einen Kommentar unter das Posting gesetzt, mit der Bitte ihm zu mailen. Das habe ich gemacht und innerhalb weniger Stunden hat er eine neue Version auf der Website publiziert. Genial, unglaublich, sowas erlebt man nicht mal bei hochbezahlten Serviceverträgen.

Structorizer

Structorizer beim Diagramme erstellen
Structorizer läuft nun wunderbar und hat seinen positiven Eindruck den ich von den Screenshots hatte bestätigt:

Structorizer unterstützt alle Sinnbilder nach DIN 66261:
Inklusive Mehrfachauswahl, Unterroutinen (Call) mit “Sub”-Darstellung, Aussprung (jump), …
Structorizer ist mehrsprachig:
Er unterstützt Englisch, Deutsch, Französisch, …
Exports:
Export als BMP-Bilder und direktes Ausdrucken.
Einfärben:
Verschiedene Farben können die Übersichtlichkeit erhöhen.

Die Bedienung ist mittels Drag-und-Drop sowie sprechenden Tooltips nach ein paar wenigen Minuten intuitiv. Es läuft stabil und das Kreieren von Struktogrammen macht einfach Spass.

Bob Fisch gebührt grössten Respekt. Ein solches Projekt in Angriff zu nehmen und auch bis zu einer verwendungsfähigen Version voranzutreiben, es der Allgemeinheit gratis zur Verfügung zu stellen (inklusive Quellcode) und dann erst noch Support für DAUs wie mich zu übernehmen ist eine Leistung die Anerkennung verdient!

Teile und geniesse:
  • Technorati
  • del.icio.us
  • MisterWong
  • Digg
  • StumbleUpon
  • blogmarks
  • Furl
  • Simpy
  • Spurl
  • YahooMyWeb

Ähnliche Artikel

Eingeordnet in Desktop, Linux, Theorie und Schnipsel | 6 Komentare »

Struktogramme (Nassi Shneiderman) mit dem Computer zeichnen

Geschrieben von skaldrom am 28. August 2007

Struktogramme

Struktogramme sind einiges platzsparender als Flussdiagramme. Gerade für Programmierneulinge ist es eine gute Möglichkeit Abläufe zu visualisieren.

NSDs können natürlich von Hand mit Stift auf Papier gezeichnet werden. Allerdings nur, wenn man ein Mensch mit exakter Schreibweise und ruhigem Strich ist. Bei Sauklauen wie mir erinnern die Struktogramme eher an moderne Kunstwerke als an Algorithmen. Computerapplikationen helfen gut in diesen Fällen und so habe