Microframework: Silex

Allgemeines
Das PHP Microframework Silex des Symfony2 Erfinders Fabien Potencier verfolgt den minimalistischen Ansatz der simplen und überschaubaren Komponenten, die aber gleichzeitig das Rad nicht neu erfinden und dem Entwickler schnell ein Gerüst aus getesteten, wiederverwendbaren Funktionen bereitstellt.
Zu Beginn eines Projektes steht die Frage im Raum ob man ein Framework wie CakePHP, Symfony2 oder FuelPHP verwenden sollte oder lieber eigenhändig die Programmierung der Applikation übernimmt. Für kleinere Webapplikationen sind große Frameworks zu monströs um sinnvoll und vor allem kostengünstig eingesetzt werden zu können. Programmieren „from scratch“ ist jedoch auch keine vernünftige Alternative da man hier schnell die dreihundertste Datenbankklasse programmiert.
Microframeworks wie Silex bieten hier einen passenden Mittelweg aus Framework und Freiheit des Programmierers.
Bei Silex z.B. muss nur die silex.phar Datei ins PHP Gerüst inkludiert werden und schon kann der Programmierer drauf los legen. Dies beweist der folgende Code:

require_once __DIR__.'/silex.phar';  

$app = new Silex\Application();
$app->get('/hello/{name}', function($name) use($app) {
    return 'Hello '.$app->escape($name);
});

$app->run();

Im Folgenden werde ich in Auszügen ein Beispielprojekt mit Hilfe des Silex Microframeworks vorstellen. Das gesamte Beispielprojekt kann als ZIP Datei heruntergeladen werden und auf einem Server mit Apache, MySQL und PHP 5.3 ausgeführt werden.

Struktur
Die hier exemplarisch dargestellt Struktur ist NICHT bindend und hat sich bisher beim entwickeln bewährt.

/app
    /views
    /repositories
/cache
/config
/public
    /css
    /img
    /js
/vendor
    /Silex
    /Twig

Der vhosts Eintrag des apache Webservers sollte hier auf den /public Ordner zeigen. Der eigentliche Programmcode wird ins /app Verzeichnis ausgelagert.

bootstrap.php
Die bootstrap.php im /app Verzeichnis ist das Herzstück der Anwendung. Diese enthält alle Routing Informationen und den passenden Controller Code. Am Beispiel der /complete Funktion erläutere ich einige grundlegende Funktionen des Frameworks:

/**
 * Route: /complete/(id)
 * Sets a task as completed
 * @param int id
 */
$app->get('/complete/{id}', function ($id) use ($app) {
    $tasksRepository = new TasksRepository($app['pdo']);
    $tasksRepository->complete($id);

    return $app->redirect('/');
})
->assert('id', '\d+');

Die $app->get(…) Funktion ist eine Abkürzung der $app->match(…)->method(‘get’) Methode. Hier werden alle GET-Anfragen auf die URL http://example.com/complete/… abgearbeitet. {id} ist ein Platzhalter für die ID des Tasks.
Die ->assert(‘id’, ‘\d+’) Methode validiert die {id} Variable aus der URL und prüft ob die ID einer Integer Zahl entspricht. Falls nicht (falls jemand z.B. versucht SQL Injections einzuschleusen: /complete/15;DROP TABLE users;) wird die Aktion nicht aufgerufen und endet in einem 404 Fehler.

Ähnlich funktioniert dies mit POST-Aufrufen beim Speichern von Formular Daten:

/**
 * Route: /add | POST
 * Adds a task
 */
$app->post('/add', function(Request $request) use ($app) {
    $defaultFormData = array(
        'title' => null
    );

    $formData = array_merge($defaultFormData, $request->get('task'));
	// Database Actions are outsorced to an extra repository class
    $tasksRepository = new TasksRepository($app['pdo']);

    $insertedProperly = $tasksRepository->insert($formData['title']);
    if (!$insertedProperly) {
        $render = $app['twig']->render('/Tasks/add.twig', array(
            'message' => 'Task could not be saved! Title is empty'
        ));
    } else {
        $render = $app->redirect('/');
    }

     return $render;
});

Der Aufruf fügt einen neuen Task nur dann hinzu, wenn die POST Variable ‘title’ nicht leer ist. Ist diese gefüllt, wird ein neuer Task hinzugefügt und zur Startseite (‘/’) redirected. Falls nicht, wird eine Fehlermeldung auf der Formularseite dargestellt.

Der Kürze der Zeit halber habe ich hier auf den Validator Service von Silex verzichtet. Wer ihn sich genauer ansehen möchte, kann dies unter http://silex.sensiolabs.org/doc/providers/validator.html tun.

Fazit
Silex ist ein genials Framework für kleine Projekte, die in weniger als 2-3 Manntagen angefertigt werden sollen. Bei mittel bis großen Projekten sollte dagegen doch auf Symfony2 (oder einem vergleichbarem Framework) gesetzt werden.
Die einfache und schnelle Art der “Seiten”-Programmierung in Silex zeigt das Microframeworks seit PHP 5.3. sich auf jeden Fall einen Platz im Werkzeugkeller verdient haben.

Beispiel Projekt
Das Silex Taskmanager Beispiel Projekt enthält unter /sql die für die Anwendung benötigte SQL Struktur. Unter /config kann der Datenbank Zugang geändert werden.

Veröffentlicht unter Tech-Blog | Tags , , , , , | Hinterlasse einen Kommentar

Solr Suchmaschine mit Version 3.5

Solr, die Suchmaschine aus dem Apache Lucene Projekt wurde vor Kurzem in der Version 3.5 freigegeben. Viele Neuerungen und Bugfixe zeigen, dass die Apache Solr Suchmaschine für Enterprise Search Projekte geeignet ist. Weitere Informationen sind unter folgendem Link zu finden:

http://lucene.apache.org/solr/#27+November+2011+-+Solr+3.5.0+Released

Veröffentlicht unter Tech-Blog | Tags , , , | Hinterlasse einen Kommentar

Ist Twitter Bootsrap rapid?

Twitter Bootstrap ist im Prinzip ein CSS Grid, eine sinnvolle Ansammlung von Vereinfachungen und bei Bedarf eine jQuery-kompatible JavaScript Bibliothek. Klein aber nützlich. Hat der Entwickler und somit der Kunde damit Vorteile beim Entwickeln?

Ja, definitiv! Die ersten Entwicklungsschritte einer web-basierten Applikation gehen deutliche besser von der Hand. Man kann sich auf den Business-Case konzentrieren und das GUI schnell ohne Hürden aufbauen. Dabei ist es unabhängig, ob das Layout im Twitter Look&Feel bleibt oder sich nachträglich ändert.

Die unten abgebildete Demo-App wurde in Java mit dem Play-Framework entwickelt. Das selbst geschriebene CSS umfasst zwei Zeilen! Der Rest ist dank Twitter Bootstrap einfach da. Die Datenbankanwendung wurde in Summe in zweieinhalb Stunden entwickelt.

Veröffentlicht unter Tech-Blog | Tags , , , , , , | Hinterlasse einen Kommentar

Symfony2: Fehlermeldungen der Validierung übersetzen

Ein kurzes Good-to-know: Fehlermeldungen bei der Validierung können sehr einfach in Symfony2 übersetzt werden.

Beispiel ist eine Model-Klasse Car:

<?php
namespace Scandio\TestBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Car Class - brumm brumm
 * @ORM\Entity(repositoryClass="Scandio\TestBundle\Entity\CarRepository")
 * @ORM\Table(name="cars")
 */
class Car
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer", name="id")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
      * The serial number of the car
      * @Assert\NotBlank(message="The name cannot be left blank!")
      * @Assert\Regex(pattern="/^([A-Z0-9-]){1,}$/", message="Please use only capitalized letters and dashes")
      * @ORM\Column(type="string", name="serial", nullable=true)
      * @var string
      */
    private $serial;

    /* Getter and Setter Methods are hidden */
}

Wird nun ein neues Auto per /cars/add hinzugefügt, wird zuerst überprüft ob das Feld “serial” nicht leer und danach ob die Seriennummer nur aus Großbuchstaben und Gedankenstrichen besteht (Bsp: A-BZ-K55).

Um die Fehlermeldungen nun passend in eine andere Sprache zu übersetzen, muss im Projektordner für die Übersetzungen eine neue Datei “validators.{sprache}.yml” angelegt werden. “.yml” ist hier meine Präferenz. XML oder ähnliche von Symfony2 unterstützen Formate können auch gewählt werden. Die Übersetzungen liegen (meist) im Ordner /src/Scandio/TestBundle/Resources/translations.

Im Beispiel würde die Deutsche Übersetzung folgendermaßen aussehen:

# /src/Scandio/TestBundle/Resources/translations/validators.de_DE.yml
'Please use only capitalized Letters and Dashes': 'Bitte nur Großbuchstaben und Gedankenstriche verwenden'

Das wars auch schon. Ein Grund mehr Symfony2 einzusetzen!

Veröffentlicht unter Tech-Blog | Tags , , , , , , | Hinterlasse einen Kommentar

WordPress und der Balancer

Worpress ist eine wunderbare Blog-Software. Hochfrequentierte Blogs werden gerne hinter einem Balancer gehostet, so dass kein Server zu sehr belastet ist. Das ist kein Problem, bei der Auslieferung von Inhalten, aber beim anlegen?

Wie bekommt man die beiden Server dazu, dass sie synchron bleiben, obwohl unklar ist auf welchen Server neue Daten geschrieben werden, weil sie ja hinter dem Balancer sind???

Lösungsansatz: Einer schreibt, der andere ist ruhig, will heißen alle(!) Anfragen die das Schreiben betreffen werden auf einen Server geleitet, der andere ist davon nicht betroffen. – Das ist gut, wenn der Balancer URLs parsen kann, dann endet nach dem Einrichten einer zusätzlichen Synchronisation der Artikel.

Kann er aber nicht, dann muss (in unserem Beispiel) der Apache Webserver die URLs zerpflücken. Man wird schnell feststellen, dass WordPress URLs mit der “falschen” Domäne per Rewrite umbiegt, also der Client (Browser) aufgefordert wird eine andere URL zu verwenden, und da wären wir wieder beim Balancer…

Gut, wir nennen den einen Server W (für write) und den anderen RO (für read-only) die Domaine ist www.mydomain.com und machen dann folgendes auf Server W:

RewriteRule ^/(wp-(content|admin|includes|login).*) /var/www/vhosts/wordpress/$1 [L]
RewriteRule ^/[_0-9a-zA-Z-]+/(wp-(content|admin|includes|login).*) /var/www/vhosts/wordpress/$1 [L]
RewriteRule ^/[_0-9a-zA-Z-]+/(.*\.php)$ /var/www/vhosts/wordpress/$1 [L]

Das ist soweit nicht überraschend, und fügen noch folgenden Virtual Host hinzu:

<VirtualHost ...>
    ServerName bloghelper.mydomain.com
    RewriteEngine On
    RewriteRule ^/(.*)$ http://www.mydomain.com/$1 [P]
</VirtualHost>

Auf dem (den) anderen Server(n) RO führen wir nun folgendes ein:

RewriteCond %{HTTP_REFERER} /wp-admin
RewriteRule ^/(wp-(content|includes).*) http://bloghelper.mydomain.com/$1 [P]
RewriteRule ^/(wp-(admin|login).*) http://bloghelper.mydomain.com/$1 [P]
RewriteRule ^/(wp-(content|includes).*) /var/www/vhosts/wordpress/$1 [L]
RewriteRule ^/([_0-9a-zA-Z-]+/wp-(admin|login).*) http://bloghelper.mydomain.com/$1 [P]

Das sieht beinahe aus wie der Block auf dem Server W, biegt aber eben auf den Server W um. bloghelper.mydomain.de muss natürlich noch in die /etc/hosts aufgenommen werden. Die Bedingung ist da, um die Bilder nicht unnötig langasam zu machen.

Synchronisation noch anschalten. Das wär’s: Happy balancing ;-)

Veröffentlicht unter Tech-Blog | Hinterlasse einen Kommentar