PHP + mod_rewrite

Caps-Lock is FULL OF AWESOME!!1!
Attribution-NonCommercial-ShareAlike License by catcubed

Jeder WordPress Benutzer kennt die schönen Permalinks. Ein Link auf einen Artikel sieht meist nicht so aus

blogname/index.php?p=139

sondern so:

blogname/2010/04/07/Heute-ist-mal-wieder-was-tolles-passiert.

Die Technik dahinter ist recht simpel. Zum einen benötigt man das Apache2 Modul mod_rewrite und eine .htaccess Datei. Mod_rewrite leitet nach den Regeln, die man in der .htaccess Datei festlegt an eine Datei um. In diesem Fall eine PHP Datei, in der das $_SERVER Array ausgewertet wird.

Falls mod_rewrite im Apache deaktiviert ist, kann man das ganze recht simpel per Softlink einschalten:

schmiddi@dasgrauen:/etc/apache2$ cd /etc/apache2/
schmiddi@dasgrauen:/etc/apache2$ ls mods-available/ |grep rewrite
rewrite.load
schmiddi@dasgrauen:/etc/apache2$ ls mods-enabled/ |grep rewrite
schmiddi@dasgrauen:/etc/apache2/mods-enabled$ sudo ln -s ../mods-available/rewrite.load

Nun wird noch die Datei /etc/apache2/sites-enabled/000-default angepasst.

Ich habe lokal einen Apache Server, der nur der Programmiererei dient. Ich gehe hier nicht auf virtuelle Server oder sonst was ein.

Interessant sind nur die folgenden Zeilen:

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>

Ich habe das AllowOverride einfach auf All gestellt und dann den Apache neu gestartet. Nun die .htaccess Datei anlegen und befüllen:

schmiddi@dasgrauen:/var/www$ touch .htaccess
touch: kann „.htaccess“ nicht berühren: Permission denied
schmiddi@dasgrauen:/var/www$ sudo !!
sudo touch .htaccess

Um auf die index.php im Serverroot umzuleiten braucht man die folgenden Zeilen:

# BEGIN WRoot

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

</IfModule>

Bemerkenswert ist noch, das man innerhalb von IfModule kein Hash, Teppich, Schlängeli, Nummer (#) schreiben sollte, da dies Internal Errors produziert.
Nun kann man Adressen auf dem Server aufrufen, die es gar nicht gibt und wird entsprechend an die index.php weitergeleitet. Zu den htaccess Dateien wäre noch zu sagen, dass sich das ganze rekursiv auswirkt und Hierarchien möglich sind. Wenn in einem Unterordner eine weitere Datei mit Rewrite Anweisungen liegt und dieses Verzeichniss oder ein tieferes aufgerufen wird, gilt die oberste Htaccess Datei.

Nun kann man sich die Sache unter PHP anschauen. An die Requesturl kommen wir wie gesagt mit dem $_SERVER Array:

$request = $_SERVER['REQUEST_URI'];

Wie so oft bei PHP bekommt man Probleme, wenn Sonderzeichen ins Spiel kommen:

Die Url:

http://localhost/Frühling/läßt/sein/blaues/Band

verwandelt sich in:

/Fr%C3%BChling/l%C3%A4%C3%9Ft/sein/blaues/Band

Um auswertbare Zeichenketten zu bekommen,  muß die Url dekodiert werden und der String nach UTF-8 gewandelt werden:

echo utf8_decode(urldecode($request));

Nun wird noch ein Array daraus gebastelt um die Urls bequem verarbeiten zu können:

function get_url(){
	$request = $_SERVER['REQUEST_URI'];
	$crumbs = explode('/' , utf8_decode(urldecode($request)));
	$retval=array();
	foreach ($crumbs as $crumb){
		if ( trim($crumb) !='')
			array_push($retval, $crumb);

	}//each
	return $retval;
}//function

Seehr schön. Um nun sinnvoll mit Permalinks zu arbeiten, muß man sich noch Gedanken über das Erstellen und Auswerten machen. Zuerst sollte man sich Gedanken über den Aufbau machen. Hier schauts so aus:

Jahr/Monat/Tag/Titel

Das erzeugen der Adresse erledigt die Funktion generate_url mit den Argumenten Titel und gewünschtem Format:

function generate_url($title, $format){
	$d= getdate();
	$year = $d["year"];
	$month = $d['month'];
	$day = $d['mday'];
	$msg = $format;

	$msg = str_replace ('%y', $year.'/', $msg);
	$msg = str_replace ('%m', $month.'/', $msg);
	$msg = str_replace ('%d', $day.'/', $msg);
	$msg = str_replace ('%t', $title.'/', $msg);

	return "http://" . $_SERVER['SERVER_NAME']."//$msg";
}//function

Nicht sehr elegant ist der 4fache Aufruf von str_replace. Ich habe auf die Schnelle nicht herausbekommen, wie ich das eleganter hinbekomme. Über sachdienliche Hinweise zur Aufklärung würde ich mich sehr freuen

Nun kann man das ganze mal ausprobieren:

//test generate_url
$format_string='%y%m%d%t';

$url= generate_url("PHP + mod_rewrite", $format_string);
echo "$url";

Der nächste Schritt ist nun die Auswertung. Dafür werden wir wieder den Formatstring benutzen, da er uns beschreibt, was wie aus der Persistenzschicht rausgeschaufelt werden muß. Wenn z.b nur 2010 angegeben ist, bekommt man jegliche Inhalte aus diesem Jahr, bei April den Inhalt aus Jahr und Monat, wenn der Artikel angegben ist, nur der Artikel:

function get_the_content($url_array, $format) {
	$n = count ($url_array);

	/*den Formatstring in ein Array wandeln, null values entfernen
	und den Index neu aufbauen :)*/

	$f_arr = explode('%', $format);
	$empty_element = array_keys($f_arr, "");

	foreach ($empty_element as $e)
		unset ($f_arr[$e]);
	$f_arr = array_values($f_arr);

	//titel in url vorhanden?
	$t_pos =  array_search('t', $f_arr);
	if ( $url_array[$t_pos] !="")
		return  "Select Artikel: ". $url_array[$t_pos];

	//Datum Basteln
	$year = $month = $day = 0;
 	$year = $url_array[array_search('y', $f_arr)];
	$month = $url_array[array_search('m', $f_arr)];
	$day = $url_array[ array_search('d', $f_arr)];
	//Monatsname nach Zahl
	$month_number = "";
	for($i=1;$i<=12;$i++){
		if(date("F", mktime(0, 0, 0, $i, 1, 0)) == $month){
			$month_number = $i;
			break;
		}
	}//for

	if ($month_number=="")
		return "nichts gefunden";
	//Unixtimestamp erzeugen
	$d = date('c', mktime(0, 0, 0, $month_number, $day, $year));
	return "SELECT Artikel in Datumsbereich $d";
}//function

Exemplarisch habe ich einfach mal einen Unixtimestamp erzeugt und zurückgegeben. Wenn jemand das ernsthaft verwenden will, sollte er sich ein paar Gedanken über Oo machen und manches schöner schreiben.

Require, include und PHP

PHP Elephant
Attribution-NonCommercial-NoDerivs License by Laughing Squid

Ich bin noch immer am Skripten. Diesmal ein Problem, dass sich mir öfter in PHP in den Weg stellt: Dateien inkludieren. Irgendwann habe ich beschlossen nur noch require_once() zu verwenden, da Mehrfachinkludierung hier ausgeschlossen ist. Problematisch wird es, Wenn man eine Datei einbindet, die wiederrum selbst etwas einbindet. Wenn ich größere Sachen schreibe, Habe ich eine Datei, in der es nur requires gibt um mir Redundanzen in der Schreibareit zu sparen. Wenn ich diese Datei jedoch aus verschiedenen Verzeichnissen einbinde, kann es schon mal zu Fehlern kommen, wenn die Pfadangaben relativ gemacht werden. Das Problem schaff man recht bequem mit diesen 4 Zeilen aus der Welt:

$d =   dir(dirname(__FILE__));
chdir($d->path);
chdir ('../');
$basedir= getcwd();

require_once("$basedir/settings.php");
require_once("$basedir/elementar/CSettings.php");
require_once("$basedir/additional/CFormHandler.php") ;
require_once("$basedir/core/CRegister_Hooks.php");

Die Funktion dirname() gibt den absoluten Pfad der Konstante __FILE__ zurück – in diesem Fall den der Datei requirerequire.php (der Name ist nicht nur aussagekräftig sondern auch cool). Da die Datei in /core liegt, wechsle ich ins Basisverzeichniss und muß nur noch daran denken, die absoluten Angaben zu machen. Wenn jemand einen eleganteren Trick kennt, soll er ihn mir bitt mitteilen.:)

WordPress Exportfile mit PHP verarbeiten

Biere 333 Export
Attribution-NonCommercial-NoDerivs License by Kris_B

Momentan skripte ich relativ viel. Dabei bin ich auf das Problem gestoßen, den XML Export von WordPress in eine andere Datenbankstruktur zu quetschen. Als Hilfe dient mir dabei ein wenig oO Ekel-Code, der per simpleXML der das File parst und alles relevante in ADT’s (strcuts mit get + set) drückt, damit man leichter weiterkommt. Wers will, hier ist es. Zum Skript ist nicht viel zu sagen, es gibt Unmengen an ADT’s mit redundanten Funktionen und ein paar Arrays die damit gefüllt sind. Am Besten schaut man sich die Chose mit dem Classbrowser einer beliebigen PHP IDE an. Vielleicht erspare ich damit jemandem eine Stunde Arbeit. 🙂

Plugins für WordPress schreiben (diesmal Twitter)

Power Plugs
Attribution-NonCommercial License by Gone-Walkabout

Heute mal  etwas in eigener Sache. Der Artikel hat zwar (leider) nicht soviel mit Ubuntu zu tun, aber vielleicht interessiert es doch den ein oder anderen auif dem UU Planeten.Vor Ewigkeiten hatte ich mal 2 Plugins für WordPress geschrieben und ich muß sagen, daß das eine recht angenehme Sache ist – auch für Leute die sich eher selten mit Skriptsprachen rumschlagen. Die englischsprachige Doku ist sehr gut, die Methoden logisch und einfach zu implementieren. Da ich massive Probleme mit Twitter und meinem Hoster hatte (hier und auch da) entschloss ich mich mal wieder ein Plugin zu schreiben. Zu finden ist das gute Stück namens schmie_twitter im Plugin Directory. Die Funktionen beschränken sich auf ein Minimum:

  1. Auf Twitter veröffentlichen bei neuem Post
  2. Auf Twitter veröffentlichen bei Aktualisierung eines Posts
  3. Twitternachricht vorkonfigurieren. (Hallo bei %blog gibt Neuigkeiten: %Post, %url)
  4. Url kürzen (ein paar Dienste inkl. bit.ly vorhanden).

Der Clou an der Sache ist, dass man sowohl per HTTP  mit Twitter reden kann, als auch per Email (Twittermail); wenn der Hoster Twitter ausgesperrt hat. Was noch fehlt ist ein wenig i18n ansonsten wars das. Wenn  Interesse an einem Artikel über Pluginprogrammiererei besteht oder jemand etwas auf der Seele hat wegen schmie_twitter einfach einen Kommentar hinterlassen.

Update: jetzt auch schon fast mit Identi.ca Support. Super

Twitter und Byethost

Bekanntlich möchte mein Webhoster ja nicht mit Twitter. Eben mal ein wenig analyisert. Laut phpinfo(); ist Curl aktiviert, funktioniert recht normal, nur nicht mit Twitter und Facebook. Gethostbyname schafft Gewissheit:

Twitter?: <?php echo gethostbyname(‚twitter.com‘); ?><br/>
Facebook?: <?php echo gethostbyname(‚facebook.com‘); ?><br/>
Google?: <?php echo gethostbyname(‚google.com‘); ?><br/>
Byet.net?: <?php echo gethostbyname(‚byet.net‘); ?>

Hier die Ausgabe

Twitter?: twitter.com
Facebook?: facebook.com
Google?: 209.85.225.99
Byet.net?: 173.45.83.131

Wenn der Name nicht aufgelöst wird, klappts nicht. Also gilt es Alternativen zu finden.
Ich kann zwar erfolgreich ein Curl auf eine Twitter IP machen, allerdings klappt das Posten nicht, da bei Twitter anscheinend geclustert / geloadbalanced (geiles Wort:)) wird und ein einzelner Server meine Anfrage nicht verarbeiten will:
schmiddi@nohost:~$ ping -c 1  www.twitter.com
PING twitter.com (168.143.162.68) 56(84) bytes of data.
64 bytes from 168.143.162.68: icmp_seq=1 ttl=247 time=208 ms
— twitter.com ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 208.490/208.490/208.490/0.000 ms
schmiddi@nohost:~$ ping -c 1  www.twitter.com
PING twitter.com (168.143.161.20) 56(84) bytes of data.
64 bytes from 168.143.161.20: icmp_seq=1 ttl=247 time=236 ms
— twitter.com ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 236.249/236.249/236.249/0.000 ms
schmiddi@nohost:~$ ping -c 1  www.twitter.com
PING twitter.com (168.143.162.100) 56(84) bytes of data.
64 bytes from 168.143.162.100: icmp_seq=1 ttl=247 time=213 ms
— twitter.com ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 213.934/213.934/213.934/0.000 ms
Verdammt. Laut Hoster wurde der Zugriff auf solche Seiten gesperrt, da allerhand Idiotenvolk über Byethost dort SPAM verbreitet hat.
Mit Twitterfeed funktionierts über Umwege. Twitterfeed prüft ungefähr stündlich den RSS Feed und veröffentlicht Neues via Twitter. Das nicht zeitnah veröffentlicht wird, ist zwar nicht lebensnotwendig, stört jedoch den Kontrollfreak.Leider hat man auch keine Möglichkeit die Posts zu manipulieren.  Posttitel + gekürzte URL, das wars. Klappt zwar, ist aber nicht das Wahre.

Ich brauche eine Art Broadcast-Dienst, dessen Adresse hier nicht gesperrt ist. Ich stell mir das so vor, dass ich diesem Dienst eine Nachricht sende und dann wird es auf den unterschiedlichsten Diensten (u.A. Twitter) veröffentlich.

Leider, leider bin ich noch immer auf der Suche nach sowas, aber vielleicht findet ich ja noch eine schöne Quick & dirty Lösung für PHP :).

WordPress Workarounds…

Es fing alles recht harmlos an. Schon seit geraumer Zeit (v.2.7*) hat mich WordPress dezent darauf hingewiesen, dass eine neuere Version vorliegt. Da meine Autoupdate Funktion vor langer Zeit den Geist aufgegeben hat, war mir das recht egal. Nun ließen sich aber auch keine Updates mehr automatisch einspielen. Irgendwann gab’s dann auch Probleme mit den Feeds. Dazu muß ich sagen, dass ich mal einen halben Tag weggeworfen habe, nur um automatisch Plugins installieren und Updaten wieder an den Start zu bekommen. Nochmal wollte ich mir das nicht antun. Ob das alles nun an mir liegt, WordPress oder meinem bizarren Gratis-Webhoster vermag ich nicht zu sagen. Naja egal heute Mittag wollte ich mir ein Twitter Plugin installieren. Um nicht zuerst das Plugin runterzuladen und dann mit 50 – 200 b/s – jaaa nicht Kilobyte sondern Byte per FTP zu uppen (wie gesagt sehr bizarrer Hoster) dachte ich mir komm mach der Sache ein Ende und spiel alles neu auf.

Unterm Strich hat der tolle OnlineFTP Manager nicht nur meine htdocs nach /dev/null oder ins Nirvana der Free Webhoster kopiert. Der  FTP Upload klappt mal wieder nicht=>keine Plugins oder Updates automisch einspielen. Diesmal half es diese 4 Zeilen an die wp-config.php zu hängen (ein normales chmod per FTP Manager reicht nicht):

if(is_admin()) {
add_filter('filesystem_method', create_function('$a', 'return "direct";' ));
define( 'FS_CHMOD_DIR', 0751 );
}

Um der Sache ein kleines Krönchen aufzusetzen klappt nun mein Flickrplugin auch nicht mehr und das Twittern, um das es eigentlich ging will auch nicht und ich habe keine Ahnung warum. 🙁

Beim Twittern liegts wohl dran, das neuerdings die XML RPC / curl Erweiterung in PHP deaktiviert ist.

Kennt von euch jemand einen einigermaßen guten Gratis-Hoster oder hat gar wer einen Tipp für mich?

ln

Oh Mann! Es ist zwar schon ein wenig später, aber so verpeilt war ich selten. Links auf Verzeichnisse sollten in Linux immer „soft“ geschehen. Guckst Du hier:

root@nethercap:/var/www# ln -s  /home/schmiddi/workspace/php_test_project/
root@nethercap:/var/www# ls php_test_project
index.php  test.html

und schon ist alles gut 🙂

Creole ist tot…

Eben mal kurz was in der Api von Creole nachlesen wollen, da las ich die böse Botschaft: Creole ist tot, keiner kümmert sich mehr drum.:(

Wirklich schade. Wem’s neu ist, Creole ist ein ziemlich netter Abstraction Layer um datenbankunabhängig und injectionsicher PHP zu Programmieren.

Es garantiert Typsichertheit (was eine Seltenheit in dieser merkwürdigen Sprache ist) und war einer der wenigen Lichtblicke für mich beim PHP Schreiben.