Schick programmiert

Sessions in PHP und ihre Tücken

Seit einiger Zeit kämpfe ich mit der Herausforderung rund um simultane Anfragen an eine PHP-Seite. Dass diese Seite mit Symfony2 läuft, hat in diesem Falle nichts zu sagen.

Diese Website hat verschiedene Bilder eingebunden, die zur Laufzeit generiert werden. Über eine .htaccess-Datei wird eine PHP-Datei angesprochen, falls eine Datei nicht existiert. War dies eine Anfrage nach einem Bild, versucht das Script die entsprechende verkleinerte Version zu generieren.

Auf dieser Seite ist auch eine Suche implementiert. Je Seite bekommt der Benutzer dort maximal 50 Ergebnisse angezeigt. Heißt – bei einem Seitenaufruf muss es möglich sein, 50 Bilder generieren zu können.

Bei meinen Tests startete jede dieser 50 Anfragen ein PHP-Script, welches zuerst geprüfte, ob der Benutzer angemeldet ist, dann das Bild generierte und ausgab.

Der Flaschenhals bei dieser ganzen Aktion machte sich deutlich, als ich begann einen PHP Profiler (xhprof in meinem Fall) zu nutzen und ein solches Szenario auf Geschwindigkeit zu testen.

Die Funktion session_start() benötigte bei fast jeder Anfrage um die 10 Sekunden – genau so lange, wie es dauerte die Bilder zu generieren.

Nach einigem Einlesen in PHP und Sessions, fand ich heraus, dass ich den standard SessionHandler von PHP nutze, der die Session in Dateien speichert. Sobald eine dieser Dateien geöffnet wurde, wird sie gelockt, sodass kein zweiter Prozess diese Datei ändern kann. Dies geschieht alles im PHP Core.

Symfony2 hat hier eine gute – aber nicht ungefährliche – Lösung gefunden, die in 95% der Fälle auch gut genug ist. Sie erstellen einen eigenen SessionHandler, der die Werte aus dem Speicher (Datei, Memcache(d), Redis usw.) ausliest, diese aber nicht für weitere Prozesse sperrt. Die Funktion, in der PHP die Sperre legt, ist hier beschrieben: http://de1.php.net/manual/en/sessionhandlerinterface.read.php

Hier noch ein Link zu den genannten Implementierungen des SessionHandlers für Symfony2 – zum Abgucken für dein eigenes System ;) https://github.com/symfony/symfony/tree/master/src/Symfony/Component/HttpFoundation/Session/Storage/Handler

Das Problem, welches sich hierbei aber ergibt ist, wenn man mehrere Prozesse auf die gleiche Session zugreifen lassen möchte, und beide diese Session bearbeiten, werden (theoretisch) die Änderungen des letzten alle Änderungen der vorigen Prozesse überschreiben.

Dazu auch ein Link: http://www.matt-knight.co.uk/2011/concurrent-php-sessions/
Auf dieser Seite findet ihr auch viel Dokumentation zu diesem Thema.

Meine Anwendung fiel hier auch unter die 95%. Wenn jemand noch mehr Hintergrundwissen braucht, so habe ich einiges mehr an Informationen zu diesem Thema in diesem Post verfasst: http://forum.nginx.org/read.php?3,198958,240954#msg-240954
Andernfalls hilft auch eine Suche mit den Stichworten “php session blocking”.


Mailserver mit Fallback einrichten

Auch wenn ich sonst immer etwas gepostet habe, werde ich hier nur eine Idee niederschreiben, die ich zur Zeit leider noch nicht umsetzen werde.

Auf dem Plan steht ein Mailserver. Hauptkriterium für einen Mailserver, für mich, war, dass er immer erreichbar ist. Eine Frage bei Stackoverflow ließ mich dieses Kriterium noch einmal überdenken … http://serverfault.com/questions/303554/how-to-build-a-high-availability-postfix-system#answer-303608

In diesem Artikel werden zwei Wege beschrieben: Der erste besagt nur, dass zu jeder Zeit Mails angenommen werden können. Dafür kann man einen ganz normalen MX Backup Server einrichten. Der zweite Weg besagt allerdings, dass auch die IMAP und POP3 Dienste jederzeit erreichbar sind. Das kann schnell schwierig werden, da man die Mailverzeichnisse ständig up-to-date halten muss und bei bzw. nach einem Ausfall diese Mailverzeichnisse wieder abgleichen.

Ich entschied mich also den ersten Weg einzuschlagen und meinen Fokus damit darauf zu setzen, dass der Mailserver immer erreichbar ist.

Die Struktur, habe ich gedacht, könnte aus 3 Servern bestehen:

  1. Der Master Mailserver mit Postfix und Dovecot installiert, der die Hauptarbeit verrichten soll
  2. Ein Backup Mailserver (gleiche Konfiguration wie der Master), der die Mails nur in einer Mail-Queue halten soll, wenn der Master nicht erreichbar ist um sie ihm später zuzustellen
  3. Webserver für eine Webmailoberfläche und PostfixAdmin

Alle diese Server sind virtuelle Maschinen, von denen täglich ein Komplett-Backup erstellt wird. Master- und Backup-Mailserver sind dabei auf zwei getrennten reellen Maschinen installiert.

Um im Falle eines Festplattencrash die Mailpostfächer trotzdem noch zu haben, wird eine sekündliche Synchronisation der Postfächer vom Master auf den Backup-Mailserver durchgeführt.

Wenn jemand dies durchführen will, setze ich hier noch einige Links dran, bei denen ich meine Hilfe bereits zusammengesucht habe. Leider wurde dieses Projekt auf Eis gelegt, da es für unsere Firma (noch) nicht wirtschaftlich ist. Ein bisschen muss man auch an solche Sachen denken …

Master Mailserver konfigurieren: http://workaround.org/ispmail/squeeze

Backup Mailserver einrichten: http://www.howtoforge.com/postfix_backup_mx, http://www.cyberciti.biz/faq/postfix-backup-mx-server-anti-spam/

Plugin für RoundCube, womit Benutzer ihr eigenes Passwort ändern können: http://trac.roundcube.net/browser/github/plugins/password/README

PostfixAdmin – zur Administration von Domains und deren Mailadressen: http://postfixadmin.sourceforge.net/

Einfache Beispiele mit RSYNC: http://www.thegeekstuff.com/2010/09/rsync-command-examples/


Transliteration und Romanisierung von Texten

In einem meiner aktuellen Projekte geht es um Dokumentenarchivierung – international. International bedeutet hier nicht nur, dass man diesen Dienst in jedem Land nutzen kann und alle Texte entsprechend übersetzt sind, sondern auch, dass man Texte in verschiedenen Sprachen eingeben und suchen kann.

Einige dieser Texte enthalten Sonderzeichen, die Sprachspezifisch sind. Im Deutschen kennen wir alle die Umlaute ä, ö, ü und ß. Das ist aber erst der kleine Anfang. Im Norwegischen gibt es die Sonderzeichen å, æ und ø.

Nun kann es vorkommen, dass eine deutsche Person ein Dokument archivieren soll, wo in der Beschreibung einer dieser norwegischen Buchstaben drin steht – oder sie sucht nach einem solchen Dokument.

Was ist nun eine gute Möglichkeit, diese Sonderzeichen zu “normalisieren”? Oder auf jeden Fall so zu schreiben, dass jeder sie schreiben kann – egal welches Keyboard-Layout er eingestellt hat?

Und was mache ich, wenn ich eine Suchmaschine, wie Elasticsearch, nutze, die in der genutzten Version nur Suchanfragen mit Wildcards unterstützt, wenn man keine Sonderzeichen in der Suchanfrage hat?

Ein ganz anderer Anwendungsfall für das gleiche Schema ist das generieren einer URL. Auch dort ist es ratsam meist lateinische Buchstaben zu nutzen.

Die Lösung ist einfach: Romanisierung.

Hinter diesem Wort stehen gleich mehrere Möglichkeiten. Die eigentliche Bedeutung kommt daher, dass die Römer viele andere Völker und Länder besetzt haben und ihnen ihren Glauben und ihre Sprache und Schreibweise aufgezwungen haben. Linguistisch ist damit gemeint, dass jeder Umlaut in eine passende Buchstabenfolge umgewandelt wird, sodass der gesamte Text mit den Buchstaben von A bis Z dargestellt werden kann.

Eine dieser Formen ist sprachspezifisch. Wir im Deutschen umschreiben unsere Umlaute mit ä => ae, ö => oe, ü => ue und ß => ss. Diese Umschreibung ist allerdings lokalisationsbedingt. Ich habe auch Referenzen von Zeichen gefunden, die in anderen Sprachen auf eine andere Weise romanisiert werden.

Es gibt aber auch eine standardisierte Form. Diese ist auch nicht schwer zu merken. Alle diakretischen Zeichen (¨ bei ö, ◌̸ bei ø, ´ bei é) werden entfernt, Linguaturen (œ, æ) werden getrennt geschrieben (œ => oe, æ => ae), Spezialzeichen wie ß als ss usw. Für jede Gruppe von Zeichen gibt es eine allgemein gültige Umschreibungsregel.

Um es noch interessanter zu machen: Romanisierung, wie hier beschrieben, gilt nur für das lateinische Alphabet. Jetzt gibt es aber auch unterschiedliche Schriftsysteme … Alphabete wie Kyrillisch, Armenisch, Griechisch oder Konsonantenschriften wie Arabisch und Hebräisch, die relativ einfach zu übersetzen sind, aber auch logographische Sprachen wie Hangeul oder Kanji, die zum Beispiel in Japan, Korea und China genutzt werden.

Alle diese Schriftsysteme kann man auch auf unterschiedliche Weise in das lateinische Alphabet übersetzen.

Entweder man macht es lautgetreu (Transkription genannt) – wobei es hier auf die Quellsprache und die Zielsprache ankommt. Ein Beispiel ist der Buchstabe И aus dem kyrillischen Alphabet. Er wird im Russischen wie ein deutsches i ausgesprochen, im Ukrainischen aber wie ein y. Das gleiche ist im Lateinischen, wo der Laut sch im deutschen sh im englischen geschrieben würde. Diese Möglichkeit eignet sich also nicht für etwas automatisches wie URL-Generierung, wenn man keine Lokalisationsdaten hat.

Transliteration ist ein anderer Ansatz, der die genaue Übersetzung von Zeichen zu Zeichen vorschreiben. Hierbei wird nicht viel Wert auf die genaue Aussprache des Wortes, aber des Buchstaben gelegt.
Ein Beispiel hierfür (kyrillisch zu lateinisch): жина -> žena

Für mich war die Kombination aus Transliteration und Romanisierung passend. Damit bekam ich alle Buchstaben in eine lautähnliche Variante des lateinischen Alphabetes in romanisierter Form. Das Beispiel жена wird somit zu zena.

Hier noch der dazu passende PHP Code:

echo transliterator_transliterate('Any-Latin; Latin-ASCII', "жена");
// zena

Wenn jemand eine Transskription braucht, sollte er sich die Funktion iconv() ansehen, welche auf Basis der gesetzten Lokalisation arbeitet. Das verlinkte Kommentar zur Funktion iconv auf der php.net Seite hat mehr Informationen zu diesem Thema.

Quellen:

 


Automatisches Update für phpMyAdmin

Everyday’s a school day – man lernt doch nie aus. Die letzte Woche war ich damit beschäftigt einen Webserver zu installieren und habe dabei einige Probleme entdeckt, wo ich mich durchbeißen musste.

PhpMyAdmin braucht fast jeder, der MySQL als Datenbank verwendet – aber keiner will es aktuell halten – weil es doch eine ständige Aufgabe ist. Es geht aber auch einfach.

In dem Repository von phpMyAdmin gibt es, laut dem hier verlinkten Artikel, schon lange einen Branch mit dem Namen STABLE. Wie es aussieht, werden dort immer die aktuellen Versionen rein gemerged und getaggt. Somit braucht man, bei einer Einstellung wie der im folgenden Link beschrieben, nur noch den Befehl git pull und die phpMyAdmin Installation ist wieder auf dem aktuellen Stand.

http://linux.wxs.ro/2012/12/14/git-update-phpmyadmin/

Wer will, kann es sich das dann auch als täglichen cron-job einrichten. Hier ist mein Script:

#!/bin/sh
cd /path/to/webroot/phpMyAdmin && sudo -u www-data git pull >> /dev/null


Service, Hook, XCLASS extension usw in TYPO3

In TYPO3 gibt es 6 verschiedene Variationen um Extensions oder den Core selbst zu erweitern oder Funktionen abzuändern ohne deren Code direkt anzufassen:

Hier ist eine persönliche Zusammenfassung, wann ich welche Methode wähle. Jede der Methoden hat ihre Vor- und Nachteile.

Service

Durch das Schreiben eines neuen Services kann man einen Dienst, der als Service geschrieben wurde, durch eine eigene Implementierung ersetzen.

Eine Besonderheit hierbei ist, dass jeder Service in Sub-Services aufgeteilt werden kann. Jeder dieser Sub-Services kann, unabhängig vom Rest des Services, überschrieben werden.

Ein gutes Beispiel für einen Service in TYPO3 ist die Anmeldung. Zu diesem Service konnte ich keine Dokumentation finden, daher verweise ich hier auf die Dokumentation von cc_sv_auth. Dieser Service wird, laut Dokumentation, in Backend und Frontend, und Benutzer bzw. Gruppen hohlen und authentifizieren getrennt. Durch diese Trennung ist es zum Beispiel möglich, jeden Benutzer über das gleiche Passwort zu verifizieren, wie es in cc_magicpw umgesetzt wurde. Ich hoffe stark, dass diese Erweiterung nur als Beispiel dient, und nicht produktiv eingesetzt wird.

Hook

Durch einen Hook können an einer, vom Programmierer vorgesehenen, Stelle Variablen geändert oder Funktionen hinzugefügt werden. An einer solchen Stelle können beliebig Implementationen geladen werden.

XCLASS extension

Die XCLASS extension ist die älteste, TYPO3 bekannte, Form die Funktionalität einer Erweiterung zu ändern – sie ist damit auch die unflexibelste. FYI: Implementation für XCLASS extensions wurde zum Wechsel auf TYPO3 v6.0 geändert (siehe Link zur Dokumentation in der Überschrift).

Bei dieser Variante wird die PHP-Klasse erweitert. Da PHP keine Mehrfachvererbung unterstützt, stellt sich hier schnell die Frage, was passieren soll, wenn zwei Personen die gleiche Erweiterung an unterschiedlichen Stellen ändern wollen. Ein Beispiel dafür ist die Erweiterung tipafriend, welche durch tipafriend_captcha mit Hilfe einer XCLASS die Funktion des Captcha dazu bekommt. Alle Erweiterungen, die tipafriend auch erweitern, werden somit mit tipafriend_captcha kompatibel sein, in dem sie deren XCLASS wieder erweitern, oder nicht, wenn sie die Klasse von tipafriend erweitern.

Code des TYPO3 Core’ oder einer Extension abändern

Das ist die, bei weitem, schnellste Variante das Problem zu lösen. Problem hierbei ist nur, dass man die Erweiterung nicht mehr aktualisieren kann, ohne dass der eigene Code verloren geht. Deswegen ist diese Variante auch wirklich als aller, aller letzte Möglichkeit zu sehen.

Wann nutze ich was?

Services und Hooks verfolgen im Ansatz ganz unterschiedliche Ziele. Je nach dem, ob ein Service oder Hook existiert, wird dieser in meinen Entwicklungen bevorzugt verwendet. Hierbei achte ich auch (speziell bei Hooks), wofür der Programmierer ihn gedacht hat. Dementsprechend kann ich auch erwarten, dass meine Implementation in der nächsten Version noch funktionieren wird.

Gibt es keine Hooks oder Services, muss ich zur XCLASS extension zurückgreifen. Nicht schön – aber selten. Diese speichere ich vorzugsweise in einer separaten Erweiterung, welche mit dem Status experimental gekennzeichnet wird. Der Key der Erweiterung beginnt immer mit user_ und enthält den Namen der Erweiterung(en), von denen eine XCLASS erstellt wurde, und die ID des Eintrages im BugTracker. Passen diese Daten nicht in den Key, wird dieser auf andere Weise gut formuliert und die Liste der Erweiterungen und die ID kommen in die Beschreibung. Alle Erweiterungen, auf die diese Erweiterung aufbaut, werden in die constraints als depends mit genauer Versionsnummer oder Versionsbereich aufgenommen.

Hier noch kurz als Beispiel die Konfigurationsdatei einer von mir erstellten XCLASS für crawler, die mit einer bestimmten Version von realurl nicht mehr funktioniert:

ext_emconf.php

/***************************************************************
 * Extension Manager/Repository config file for ext "user_crawler_realurlfix".
 *
 * Auto generated 15-01-2013 15:30
 *
 * Manual updates:
 * Only the data in the array - everything else is removed by next
 * writing. "version" and "dependencies" must not be touched!
 ***************************************************************/

$EM_CONF[$_EXTKEY] = array(
	'title' => 'Crawler-width-realurl xClass',
	'description' => 'xClass for Crawler using realURL (http://forge.typo3.org/issues/44554)',
	'category' => 'misc',
	'shy' => 0,
	'version' => '0.0.0',
	'dependencies' => 'crawler,realurl',
	'conflicts' => '',
	'priority' => '',
	'loadOrder' => '',
	'module' => '',
	'state' => 'experimental',
	'uploadfolder' => 0,
	'createDirs' => '',
	'modify_tables' => '',
	'clearcacheonload' => 0,
	'lockType' => '',
	'author' => 'Simon Schick',
	'author_email' => 'ssc@softvision.de',
	'author_company' => '',
	'CGLcompliance' => '',
	'CGLcompliance_note' => '',
	'constraints' => array(
		'depends' => array(
			'crawler' => '3.0.0-3.3.0',
			'realurl' => '1.8.0-1.10.2',
		),
		'conflicts' => array(
		),
		'suggests' => array(
		),
	),
	'_md5_values_when_last_written' => 'a:3:{s:17:"ext_localconf.php";s:4:"1a47";s:34:"xClass/class.ux_tx_crawler_lib.php";s:4:"12d0";s:30:"xClass/class.ux_tx_realurl.php";s:4:"bc30";}',
	'suggests' => array(
	),
);

Noch seltener kommt es vor, dass ich Erweiterungen dort überarbeiten muss, wo es auch keine XCLASS gibt. Ist dies der Fall bei einer installierten Erweiterung, wird diese mit dem Status excludeFromUpdates in der Datei ext_emconf.php gekennzeichnet.

Zum Weiterlesen:


Unterschiedliche Fehler beim Hochladen einer Datei …

Ich hatte heute den Fall, dass ich beim Hochladen von unterschiedlich großen Dateien auch unterschiedliche Fehlermeldungen in unterschiedlichen Objekten in meiner Symfony2 Form bekommen habe.

Die kleinere Datei (3MB) kam mit der Fehlermeldung “The file is too large. Allowed maximum size is X bytes.“. Diese Fehlermeldung war, wie erwartet, im Objekt für das Element zum FileUpload gesetzt.
Bei der größeren Datei (15MB) hingegen bekam ich die Fehlermeldung “The uploaded file was too large. Please try to upload a smaller file.“, welche für das Form Objekt gesetzt war.
Read the rest of this entry »


Die Idee von n-n relations und MySQLs GROUP_CONCAT

Ich bin einige Zeit mit dem Gedanken gegangen, wie man Daten effektiv laden kann, die in einer NN-Verknüpfung miteinander stehen.

Letztens bin ich dabei auf die Funktion GROUP_CONCAT() gestoßen: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

Nehmen wir ein Beispiel, wo wir ein System haben, in dem eine große Liste an Artikeln verwaltet wird in einer übersichtlichen Gruppe von Kategorien.

Vorher habe ich es so aufgebaut, dass ich bei jedem Zugriff auf die Artikel immer zwei Abfragen gesendet habe: eine um die Artikeldaten zu bekommen und eine um die zugehörigen Kategorien zu laden. Zusätzlich hatte ich eine Liste aller Kategorien im Speicher, da sie im weiteren Verlauf des Scripts immer wieder gebraucht wurden.

Warum nicht alle Kategorien am Anfang in den Speicher laden (vielleicht zusätzlich als Objekte serialisieren und in einem Speicher wie Memcache lagern) und mit nur einer Abfrage mehrere Artikel und deren Kategorien zu laden?

Ein SQL-Befehl zu dem gegebenen Beispiel würde ungefähr so aussehen:

Zuerst alle Kategorien laden:

SELECT * FROM category;

Dann kommt die Liste der Artikel mit Kategorie-Ids (als csv):

SELECT article.*, GROUP_CONCAT(article_has_category.category_id)
  FROM article LEFT JOIN article_has_category ON (article.id = article_has_category.article_id)
  GROUP BY article.id;

Der gesamte PHP-Code könnte dann so aussehen:

class Article {
    private $_categoryIds = array();

    public function __construct($row) {
        $this->_categoryIds = explode(",", $row['category_ids']);
    }

    public function getCategories() {
        $cat = array();
        foreach($this->_categoryIds as $id)
            $cat[] = CategoryRepository::getCategoryById($id);
        return $cat;
    }
}

class Category {}

class CategoryRepository {
    private static $objects;

    public static function load() {
        $res = mysql_query("SELECT * FROM category");
        while(($row = mysql_fetch_array($res))) {
            self::$objects[$row['id']] = new Category($row);
        }
    }

    public static function getCategoryById($id) {
        return self::$objects[$id];
    }
}

class ArticleRepository {
    public static function getArticles() {
        $objects = array();
        $res = mysql_query("SELECT article.*, GROUP_CONCAT(article_has_category.category_id) AS 'category_ids' FROM article LEFT JOIN article_has_category ON (article.id = article_has_category.article_id) GROUP BY article.id");
        while(($row = mysql_fetch_array($res))) {
            $objects[] = new Article($row);
        }
        return $objects;
    }
}

mysql_connect('localhost', 'root', 'root');
mysql_select_db('testdb');
CategoryRepository::load();
$articles = ArticleRepository::getArticles();

foreach($articles as $article)
    $article->getCategories();

Wer das gleich ausprobieren will, kann diese Testdaten verwenden:

CREATE TABLE IF NOT EXISTS `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;

INSERT INTO `article` (`id`, `title`) VALUES
(1, 'Lorem Ipsum'), (2, 'Cooler Collins');

CREATE TABLE IF NOT EXISTS `article_has_category` (
  `article_id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `article_has_category` (`article_id`, `category_id`) VALUES
(1, 1), (1, 3), (2, 3);

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ;

INSERT INTO `category` (`id`, `title`) VALUES
(1, 'Gruppe1'), (3, 'Gruppe2');

CREATE TABLE IF NOT EXISTS `friendships` (
  `initiator_id` int(11) NOT NULL,
  `reciprocator_id` int(11) NOT NULL,
  UNIQUE KEY `initiator_id` (`initiator_id`,`reciprocator_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Diese Funktion hat nicht nur MySQL sondern auch PostgreSQL – nur hat sie da einen anderen Namen:
http://stackoverflow.com/questions/2560946/postgresql-group-concat-equivalent

Für MSSQL sieht es hier schwierig aus. Wenn sich die Situation noch nicht geändert hat muss ein Workarround geschrieben werden:
http://stackoverflow.com/questions/451415/simulating-group-concat-mysql-function-in-ms-sql-server-2005


Htaccess Manager in PHP

Eines meiner Projekte teilt sich in mehrere Subdomains auf von denen jede über einen Passwortschutz gesichert ist.

Damit ich die Passwörter der Personen nicht alle selbst pflegen muss, habe ich über eine Möglichkeit nachgedacht ein Script zu schreiben, wo diese Benutzer selbst ihr Passwort nach belieben abändern können.

Da ich selbst noch etwas zwischen Apache und Nginx hinke habe ich diese Implementation so gestaltet, dass PHP die gesamte Anmeldung kontrolliert.

Link zum Projekt: https://github.com/SimonSimCity/Htaccess-User-Manager

Die Applikation ist bisher recht einfach aufgebaut. Wenn sich ein Benutzer anmeldet (Benutzer und Passwort werden aus einer registrierten htaccess Datei geladen) bekommt er ein Formular in dem er sein Passwort ändern kann. Der Admin bekommt zusätzlich eine Liste aller registrierten Benutzer angezeigt.

Zukünftige Funktionen:

  • Admin kann Benuzter hinzufügen
  • Admin kann Benutzer löschen
  • Admin kann Passwörter anderer Benutzer ändern
  • SHA1 Passwörter unterstützen

Ein Problem ist auch bereits bekannt, welches aber auf einer Misskonfiguration des Apache2 beruht. Wenn ihr PHP über das FastCGI-Modul nutzt solltet ihr euch diesen Beitrag ansehen:

http://stackoverflow.com/questions/6775852/symfony-2-receiving-anonymous-token-after-basic-authentication#answers