Bernhard Häussner
Tags: Artikel mit dem Tag «PHP» durchstöbern

Howto: Alleinstehende Unds für XHTML umwandeln

16.04.2009, 13:22
$text=preg_replace ('/&(?!amp;|quot;|nbsp;|gt;|lt;|laquo;|raquo;|copy;|reg;|#[0-9]{1,5};|#x[0-9A-F]{1,4};)/','&', $text);

In meinem Markup-Parser für diesen Blog hatte ich das Problem, dass ich zwar einzelne & in &amp; umwandeln konnte, damit ich Et-Zeichen im ganz normalen Text schreiben kann, auf der anderen Seite aber auch HTML <tags> erlauben will. Wenn ich jetzt aber über HTML schrieben will, muss ich ja auch irgendwie die größer und kleiner-Zeichen encodieren, damit nicht alles einfach vom Browser gerendert wird. Wenn ich aber ein &lt; eingebe, würde das Kaufmanns-Und sofort zu &amp; umgewandelt und so würde dann im Quelltext &amp;lt; stehen und im Text nicht < sondern &lt;. Also brauchte ich eine Methode um nur einzelne kaufmännische Unds zu finden. Das lief dann auf obigen Regulären Ausdruck hinaus.

Er ersetzt & mit &amp; nur dann, wenn dahinter keines der HTML-Entities folgt. Da es eine sehr lange Liste von benannten HTML-Entities gibt, habe ich nur die aufgenommen, die man in HTML Umwandeln muss („htmlspecialchars“), damit ihre Bedeutung erhalten bleibt und ein paar Sonderzeichen, die ich sonst nie finde. Alles andere (z.B. &eacute; für é, 文字, ...) wird ja von UTF-8 abgedeckt. Für ASCII kann man auch noch die dezimale oder hexadecimale Unicode-Notierung verwenden.

Der regexp verwendet eine Erweiterung, die Negative Vorausschau (?!x). Das ist so etwas wie ein Unter-Ausdruck, der zwischendurch überprüft wird am Text nach der aktuellen Position. Die Buchstaben, die auf den überprüften Ausdrück passen, werden nicht mit "gefressen", sie werden also nicht übersprungen, sondern nur kurz getestet. Nur wenn der Ausdruck nicht zutrifft wird von der Ursprungsposition weiter getestet. Es gibt natürlich auch noch positive Vorausschau (?=x) (die weiter laufen lässt, wenn der Ausdruck passt), sowie positive (?<=x) und negative (?<!x) Zurückschau (die jeweils den Text vor der aktuellen Position prüfen).

Das würde zum Beispiel alle Minuskeln ausgeben, die nach einem „o“ stehen: [a-z](?<=o.). Man beachte den Punkt nach dem „o“. Er steht für das gerade überlaufene Zeichen für das jetzt geprüft wird was (von der aktuellen Position hinter dem Zeichen aus gesehen) davor steht. Das bedeutet in der Rücksicht sieht sich das Zeichen nochmal selbst. Noch ein Beispiel: .(?<=o(?#comment).{5}) findet alle Zeichen 5 Positionen vor „o“.

Diese Features stehen in Ruby übrigens nur teilweise zur Verfügung.

Kommentare: keine

Schöne URLs dank mod_rewrite

01.03.2009, 13:17
blogjournal.php?page=2 => blog/journal/2                   (22->14;64%)
blogtags.php?tag=PHP&page=2 => blog/tags/PHP/2             (27->15;56%)
blogentry.php?link=1_Hello_World! => /blog/1_Hello_World!  (33->20;61%)

Eigentlch war das ja schon lange geplant, alle URLs etwas zu verschönern, und sämtliche überflüssige .php?bla=&foo= rauszunehmen. Doch ich hatte immer das Problem, dass die alten URL nicht mehr benutzbar waren. Das ganze habe ich erst versucht mit RewriteRules zu lösen, doch es hat sich herausgestellt, dass es mit PHP leichter zu lösen ist. Wie alles funktioniert will ich hier kurz beschrieben.

Wer den RSS-Feed liest, dem ist vielleicht aufgefallen, dass über Nacht ein paar viele neue Einträge dazu gekommen sind. Diese sind leider gar nicht neu, jedoch da ich das URL-Format ändern musste, werden sie als neu erkannt. Die Seite hat jetzt URL-mäßig einen schönen hierarchischen Aufbau:

/
/about
/blog
/blog/1_Hello_World!
/blog/[...alle Einträge]
/blog/archiv
/blog/feeds
/blog/journal
/blog/journal/1
/blog/journal/2
/blog/journal/[...alle Seiten]
/blog/newcomments
/blog/tags
/blog/tags/PHP
/blog/tags/PHP/1
/blog/tags/PHP/2
/blog/tags/PHP/[...alle Seiten]
/blog/tags/[...alle Tags]
/contact
/projects
/projects/1_Mein_Blog

Das alles wird erstmal von mod_rewrite erledigt. Die URLs oben werden in PHP-verträgliche URLs mit GET-Paramtern usw. umgewandelt. Damit dann aber nicht immer Seiten doppelt erscheinen, musste ich im PHP-Script überprüfen, durch welche URL das Script aufgerufen wurde. Stimmt diese nich mit der kanonischen URL überein, dann leitet das Script mittels eines 301-Redirects direkt an diese weiter. So ist jedes Dokument nur einmal vertreten, was sowol meinen Aufrufstatistiken zuvorkommt, Verwirrung bei Benutzern vorbeugt und Suchmaschinen zufrieden stellt.

Außerdem sind die URLs nicht mehr von der Technik hinter den Kulissen abhängig. Sollte ich also irgendwann die Technik von PHP auf ColdFusion, ASP, Java oder was es alles gibt umstellen, bleiben die URLs (höchstwahrscheinlich) gleich. Sie enthalten keine sinnlosen Dateiendungen usw. Der Prozess funktioniert so:

                           |
                           V
              Request mit beliebiger URL
                           |
                           V
Apaches mod_rewrite wandelt in „normale“ URL mit GET Parametern um
                           |
                           V
    PHP parst die URL und entschiedet, was angezeigt werden soll
                           |
                           V
 PHP vergleicht die kanonische URL zur Anzeige mit der des Requests
             /                          \
            /                            \
    URL ist gleich                   URL ist anders
           |                              |     
           V                              V
  Content wird angezeigt         301 an die richtige URL

Im PHP sieht das Vergleichen dann ungefähr so aus: (z.B. in blog.php)

function setCanURL($root,$url) { // Leiter weiter, falls nötig
	$request_url_withoutHTTP=$_SERVER['REQUEST_URI'];
	$canonical_url_withHTTP=$root.$url;
	$canonical_url_withoutHTTP=removeDomain($root.$url);
	$request_url_withoutHTTP_realspaces=rawurldecode($request_url_withoutHTTP);
	if ($request_url_withoutHTTP_realspaces!=$canonical_url_withoutHTTP) {
			redirect301($canonical_url_withHTTP);
	}
}
function redirect301 ($newurl) { // Macht 301 Weiterleitungen
	header("HTTP/1.1 301 Moved Permanently"); 
	header("Location: $newurl"); 
	header("Connection: close");
	die();
}
function removeDomain($url) { // gibt die URL ohne den Domaine-Teil zurück
	$url=preg_replace('/https?:\/\/([0-9a-zA-Z]+\.){1,}([0-9a-zA-Z]+)\//','/',$url);
	return $url;
}
setCanURL('http://localhost/testseite/','blog');

Man muss natürlich immer irgendwie wissen, wie die kanonische URL aussehen soll. Dann ruft man einfach die Funktion auf. Es ist egal, wie die alte URL aussah! Am besten noch da einbauen, wo die URLs geparst werden bevor die Seite gerendert wird.

Super-Geeky Firefox Quicksearch feature

Die neuen URLs machen auch etwas sehr Zeitsparendes möglich. Empfiehlt sich bei allen Seiten zu machen, die man häufig benutzt (und man kurze URL-Pfade besucht):

In Firefox erstellt man im Bookmark-Manager ein neues Bookmark mit Folgenden Eigenschaften:

  • Adresse: http://bernhardhaeussner.de/%S (Großes S)
  • Schlüsselwort: bh

Allgemein funktioniert das so, dass man z.B. für Suchfunktionen „[Schlüsselwort] [Begriff]“ in die Adresszeile eingeben kann, und so eine Schnelle Suchfunktion hat. Da Firefox aber nichts anderes macht, als %S mit dem Begriff zu ersetzten, kann man das auch für jede andere URL verwenden. So kann ich jetzt durch Eingabe von bh blog direkt zu meinem Blog kommen. Sehr praktisch... Übrigens, benutzt man den Kleinbuchstaben %s wird / zu %2F und ähnliches, wodurch man nicht alle URLs eingeben kann, was jedoch in GET-Parametern wichtig ist („urlencode“).

Super-Geeky Konqueror Quicksearch feature + KRunner

Im Konqueror gibt es auch eine Suche über die Adressleiste, die sich Webkürzel nennt. Man kann diese Webkürzel in Konqueror einrichten im Einrichtungsdialog unter Webkürzel. Dort gibt man dann als Adresse http://bernhardhaeussner.de/\{@} ein und als Kürzel z.B. bh, bxt.

Selbst wenn man Konqueror nicht benutzt, lassen sich diese Kürzel in KRunner benutzten: Ein Tastendruck auf ALT+F2 und die Eingabe von bh:blog führt dann also direkt zu meinem Blog. Ziemlich praktisch und lässt sich auch beliebig erweitern.

Zum Glück ist meine Seite auch noch nicht überall verlinkt, weshalb ich auch sonst nicht viel ändern muss.

Kommentare: keine

Bilder züchten

12.02.2009, 17:32

Mit freeSq gezüchtetes Bild

Der neueste Trend ist es ja anscheinend genetisch zu Programmieren (bzw. programmieren zu lassen). Der Webcomic xkcd greift das Thema auf und Roger Alsing zeigt wie es geht. Mir geht es natürlich nicht darum Algorithmen genetisch zu basteln, oder Bilder nach zu stellen, sondern ich wollte einfach mal den Computer etwas herumprobieren lassen. Da wir in Kunst zurzeit kubistische Bilder malen müssen, habe ich mir gedacht, so etwas könnte doch auch ein Computer hin bekommen. Der Computer malt also Rechtecke, und versucht sie so hin zu bekommen, dass es ungefähr aussieht, wie das Originalbild.

Da das Vorgehen ja ein bisschen an die Biologie angelehnt sein sollte, benutzte ich so etwas, wie einen genetischen Algorithmus. (Es ist also nicht der Algorithmus, der „genetisch“ erzeugt wurde). Im ersten Versuch habe ich dem Computer fünfhundert, zunächst gleiche, weiße Rechtecke auf schwarzem Hintergrund vorgesetzt. Der Computer hat dann immer ein zufälliges Rechteck genommen und zufällig platziert. Die neuen Bilder waren also so etwas wie Kinder. Dann hat eine Fitting-Funktion überprüft, ob sich die Ähnlichkeit zur Vorlage verbessert hat. Wenn ja, hat er dieses neue zufällige Rechteck gespeichert (also in die „DNA“) aufgenommen und dieses Kind als neues Mutterelement verwendet. Hat sich nichts verbessert, hat er einfach ein weiteres Kind erzeugt.

Die Fitting-Funktion

Ich habe ein kleines Python-Script gebastelt, dass die Übereinstimmung zweier Bilder ausrechnen kann. Da alles in schwarz-weiß passiert, geht es jeden Pixel durch und addiert jeweils die Differenz der Rotwerte. Je größer die Zahl ist, desto schlechter passt das Bild. Der Computer favorisiert also Kinder mit kleineren Werten.

Zweiter Algorithmus

Da der erste Algorithmus zwar mit nur 500 Rechtecken immer bessere Näherungen erreicht hat, aber zum Ende hin sehr viele Versuche brauchte um wieder ein besser angepasstes Kind zu finden habe ich einen zweiten Algorithmus gebastelt, der diese Probleme vermeiden soll:

Er züchtet aus einem Elternelement zunächst 500 Kinder. Die Zucht-Funktion fügt allerdings jetzt jeweils ein schwarzes oder weißes Rechteck hinzu, wodurch Fehler schneller korrigiert werden können. Beim alten Algorithmus konnte ein fehlerhaftes Rechteck ja nur verschoben werden, wenn es zufällig gewählt und zufällig richtiger platziert wurde. Die Zahl der Rechtecke entspricht also der der Generation.

Die 500 Kinder sind zunächst noch klein und es werden nur 25% der Pixel gerastert. Die Kinder werden dann mit einer kleineren Version der Vorlage verglichen, und nur die besten 10 werden erwachsen und dann in voller Größe vergleichen. Damit werden nicht mehr so viel Ressourcen (Rechenzeit) für grobe Fehlplatzierungen verschwendet. Aus den letzten 10 Kindern wird dann das, welches in groß am Besten passt, als neues Elternelement gewählt, aus dem wiederum 500 neue Kinder-Bilder gezüchtet werden. Die 499 anderen Kinder werden für immer gelöscht, sodass nun nur noch die Elternelemente (die mit Verbesserungen) konserviert werden um meine Felsplatte zu schonen. So werden immer weiter bessere Merkmale vererbt, bzw. alte Fehler in den Merkmalen korrigiert.

Die DNA

Genau wie bei der echten DNA auch, werden die Informationen so gespeichert, dass die Kinder „wachsen“ können, nämlich als SVG. PHP kann durch seine DOM-Funktionen die DNA leicht verändern und die Rechtecke sind in einem angemessenen Format gespeichert. Mit dem Programm rsvg lassen sich die Rechtecke auch schnell rastern in allen beliebigen Größen.

Das Ergebnis

Der erste Algorithmus „500“ hat nach 21 Stunden 50830 Generationen erzeugt und einen dabei Das Original recht nett angenähert. Ursprünglich habe ich geplant ein kleines Video daraus zu erstellen, doch selbst bei 30 fps wäre es etwa eine halbe Stunde lang, und man würde hauptsächlich die Fehlschläge sehen. Deshalb nur einige Auszüge aus dem Werdegang:

Der zweite Algorithmus „freeSq“ ist so gebaut, dass er ohne Probleme unterbrochen werden kann und deswegen werde ich ihn wohl noch deutlich länger laufen lassen. Aber nach den ersten rund 2 Stunden und 255 Generationen (127500 Versuchen) ist das Entsanden (links Vorlage):

Seltsamerweise ist es etwas zu klobig, es wirkt bisher nicht so fein, dafür ein bisschen komplexer, doch es wird sich zeigen, wie es nach einigen weiteren Generationen aussieht. Da das Ganze sehr viel Zufall beinhaltet, könnte bei einem weiteren Durchlauf auch ein völlig anderes Bild entstehen. Ich bin gespannt.

Übrigens: So sieht Evolution bei den Simpsons aus.

Kommentare: keine

Umstellung auf PHP Data Objects mit Prepared Statements

10.01.2009, 09:52
PDO and Prepared Statements

PDO and Prepared Statements

Da ich die letzte Zeit offline war (und bald eine rund 15x schnellere Internet-Verbindung habe) konnte ich am Computer recht wenig machen. Deshalb habe ich ein bisschen an dieser Webseite gebastelt und die Datenbank-Verbindung auf PHP Data Objects (PDO) umgestellt. Diese unterstützen Prepared Statements, wo eine SQL-Abfrage schon geparsed werden kann, bevor konkrete Werte eingesetzt wurden, was sowohl der Sicherheit (keine SQL-Injections möglich) als auch dem Reccourcen-Verbrauch (Unveränderliches wird geparsed und gespeichert) zugute kommt.

Ich konnte mit der Umstellung auch gleich meine interne Datenverbindung zu den Views von einem Gemisch aus MySQL-Resultsets und Arrays auf nur Arrays umstellen, sodass die Views jetzt nicht mehr Umgeschrieben werden müssen, falls der PHP-Controller auch die Daten ändern will, wie zum Beispiel bei der Suche, wo die Reihenfolge der Ergebnisse von PHP berechnet wird. Da die MySQL-Abfragen dennoch optimiert sind, werden diese Arrays auch nicht zu groß.

Die Umsetzung der PDOs war nicht ganz einfach, zumal die (unvollständige) Datenbankabstraktion mehr oder weniger abwärtskompatibel sein sollte aber zusätzlich einiges an Automatik beinhalten sollte. So cacht sie jetzt nur SELECT-Abfragen, und gibt je nach Abfrage-Art (SELECT, INSERT, UPDATE) das Resultset, die Insert-ID oder die Anzahl der upgedateten Datensätze zurück. Sie verwaltet selbst die Prepared Statements, sodass nichts doppelt präpariert wird, überlässt mir damit also die Fütterung mit beliebigem SQL und kümmert sich um den Rest.

Da die PDOs auch eine relativ einfache Umsetzung von Transactions mitliefern, überlege ich mir, ob ich auch noch diese automatisch laufen lasse, jedoch habe ich noch kaum Erfahrung mit Transactions, weshalb ich damit erst nicht etwas beschäftigen muss (Rollbacks, SELECTs etc.).

Kommentare: keine

YaSvenT - eine PHP-GTK-Anwendung

10.01.2009, 08:46

Zugegeben - PHP ist nicht unbedingt die beste Programmiersprache wenn es um Desktop-Anwendungen geht. Doch um einen Einstieg in GUI-Programmierung zu finden, wollte ich erstmal mit einer mir gut bekannten Programmiersprache anfangen. Und mit den Objekten von PHP5 ist der Unterschied zu z.B. Java auch nicht mehr allzu groß, wenn auch deutlich spürbar. Also habe ich mich daran gemacht ein GUI-Basiertes Server-Kontrollzentrum zu basteln.

Einmal konfiguriert kann es Backups von MySQL-Datenbanken und den Dateien machen, das Error-Log anzeigen, das Access-Log in eine SQlite-Datenbank umwandeln und daraus einen HTML-Bericht erstellen, und Programme wie SSH-Konsole, Webbrowser, MySQL-Tunnel oder SFTP-Browser auf einen Klick vorkonfiguriert öffnen. Das ist recht praktisch, wenn man nicht den Ganzen Tag URLs und Befehlsfolgen tippen will. Da ich für das Meist sowieso schon Shell-Skripte hatte, lag das Ziel hauptsächlich darin, ein nettes GUI zu basteln.

Mit PHP Desktop-Anwendungen zu basteln hat den Nachteil, dass unter gängigen Linux-Distributionen zwar alles funktionieren dürfte, allerdings unter Windows PHP und GTK benötigt wird. Außerdem ist die Dokumentation von PHP-GTK noch nicht auf der vom übrigen PHP gewohnten Qualität. Einige Widgets sind nicht oder unvollständig gelistet, Beispiele sind teilweise rar und Englisch-Kenntnisse wären ratsam. Ich denke die nächste GUI-Anwendung werde ich in Java versuchen und dann vergleichen, aber mir kam es nicht so vor, als würde ich in einer Perl-Skriptesammlung für die Personal Homepage coden, und ich denke PHP ist langsam erwachsen.

Kommentare: keine
 
Χρόνογραφ
© 2008-2017 by Bernhard Häussner - Impressum - Login
Kurz-Link zur Homepage: http://1.co.de/