Persistenz und Reflexion mit PHP

City Refraction, City Reflection
Attribution License by lrargerich
Reflexion oder Introspektion bedeutet dass ein Objekt seine eigene Struktur kennt und diese Modifizieren kann.
Sehr praktisch ist dies, wenn es um Typsicherheit oder die Persistenz von Daten geht. PHP stellt dafür die Klasse ReflectionClass zur Verfügung. Ich möchte hier beschreiben, wie man mit Reflexion die Typen und Werte von Attributen auslesen kann, um diese z.B. persistent zu halten.
Zuerst ein Beispiel für Reflexion:

class ReflectionExample{
	protected $attribute1;
	protected $attribute2;
	protected $attribute3;
	public 	  $attribute4;

	public function method1(){}
	public function method2(){}

	public function __construct(){
		Reflection::export(new ReflectionClass($this));

	}
}//class
$reflect = new ReflectionExample();

Die Ausgabe:

Class [  class ReflectionExample ] {
  @@ /home/schmiddi/web/gloria2/Reflection.php 8-21

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [4] {
    Property [  protected $attribute1 ]
    Property [  protected $attribute2 ]
    Property [  protected $attribute3 ]
    Property [  public $attribute4 ]
  }

  - Methods [3] {
    Method [  public method method1 ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 14 - 14
    }

    Method [  public method method2 ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 15 - 15
    }

    Method [  public method __construct ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 17 - 20
    }
  }
}

Wie man sieht bekommt man jegliche Information über die Klasse geliefert. Ich möchte wie gesagt Daten bequem in einer Datenbank speichern, bearbeiten oder löschen. Der funktionale oder „PHP Tutorial“ Weg sähe so aus, dass man für jedes Objekt / Aspekt der PHP Seite die SQL Befehle INSERT, UPDATE und DELETE schreibt. Das ist nicht nur sehr Aufwändig, sondern benötigt auch große Aufmerksamkeit, wenn Attribute oder Variablen wegfallen oder neue hinzukommen.

Schauen wir uns noch einmal das Anfangsbeispiel an, um ein kleines Problem mit der Reflection Class zu verdeutlichen:

reflect

Die Klasse Reflect liest die Attribute der erbenden Klasse ReflectMe ein und macht eine Ausgabe:

abstract class Reflect{
	private $types = array();
	private $values= array();
	private $parentAttribute ;
	protected  $parentAttribute2;
	public function getAttributes(){
		$reflection = new ReflectionClass($this);
		$types=$this->types;
		$values=$this->values;
		foreach ($reflection->getProperties() as $key ){
			$value=str_replace("$",' ',$key->name);
			$type =  gettype($this->$value);
			$types[$key->name] = gettype($this->$value);
			$values[$key->name] = $this->$value;
			echo gettype($this->$value).
				" {$key->name} {$this->$value}
";
		}//each
	}//getAtrributes
}//class

Die Kindklasse ReflectMe hat nur ein paar Attribute die ich ausgeben möchte:

class ReflectMe extends Reflect{
	protected $child_attribute1= "Hallo Otto";
	protected $child_attribute2= 3.1415;
	protected $child_attribute3= 1337;
	private	  $child_attribute4= array();

	public function method1(){}
	public function method2(){}

}//class

$reflect = new ReflectMe();
$reflect->getAttributes();

Ein Blick in die Ausgabe verdeutlicht 2 Probleme:

string child_attribute1 Hallo Otto
double child_attribute2 3.1415
integer child_attribute3 1337
NULL parentAttribute2
Fatal error: Cannot access private property ReflectMe::$child_attribute4 in /home/schmiddi/web/gloria2/Reflection.php on line 30

Die ReflectionClass kann nicht auf private Attribute der Kindklasse zugreifen. Für meinen Geschmack ist dieser Grad der Kapselung ein wenig übertrieben. Mit Version 5.3 von PHP wird die Methode ReflectionMethod::setAccessible eingeführt, die den Zugriff erlaubt. Ein weiteres Problem wäre, dass wir auch die protected Werte der Elternklasse bekommen, da diese ja vererbt werden. Dies kann man Umgehen, indem man die Attribute von Reflect private deklariert. Damit schießt man sich jedoch in Sachen Vererbung ins Bein. Eleganter ist es die Elternklasse ebenfalls zu „reflektieren“ und die Schnittmenge der beiden Reflektionen zu entfernen:

/* ... */
	public function getAttributes(){
		//neues ReflectionObject für die Eltern Klasse erzeugen
		$reflectParent = new ReflectionClass(__CLASS__);
		$parentTypes=array();
		//Attribute in ein Array speichern
		foreach ($reflectParent->getProperties() as $key)
			$parentTypes[$key->name] = gettype($this->value);

		$reflection = new ReflectionClass($this);
		$types=$this->types;
		$values=$this->values;
		foreach ($reflection->getProperties() as $key ){
	//nur die Attribute der erbenden Klasse in die Array's schieben
			if(!array_key_exists($key->name, $parentTypes)){
				$value=str_replace("$",' ',$key->name);
				$type =  gettype($this->$value);
				$types[$key->name] = gettype($this->$value);
				$values[$key->name] = $this->$value;
				echo gettype($this->$value).
					" {$key->name} {$this->$value}
";
			}//if
		}//each
	}//getAtrributes

Nun können wir die Attribute der Klasse bequem auslesen, es sei denn sie sind als private deklariert. Für mein Skript möchte ich nicht PHP 5.3 voraussetzen, entsprechend muß darauf geachtet werden, dass nur gespeichert werden kann, was public oder protected ist.
Nun ist alles beisammen, was einen ordentlichen SQL Befehl ausmacht.
Zur Sache also:

private function generateInsertCommand(){
	$this->getAttributes();
		if ($this->insertCommand!=null)
			return $this->insertCommand;

		$classname =  $this->childClassName;
		$values = $this->attributeValues;

		$sql ="
		INSERT INTO  $classname (";
		$counter = 0;
		$count = count($this->attributeValues);
		foreach ($values as $key =>$value){
			$counter+=1;
			$sql.=$key;
			if($counter $count)
				$sql.=',';
		}
		$sql.=")VALUES (";
		$counter = 0;
		foreach ($values as $key =>$value){
			$counter+=1;
			$sql.='?';
			if($counter $count)
				$sql.=',';
		}//each

		$sql.=");";
		$this->insertCommand = $sql;
		return $this->insertCommand ;
	}//function

Die counts sind zwar nicht sehr schön, aber ich brauche sie für die Klammern. Wenn ich mal wieder ein wenig Zeit habe, stelle ich vielleicht den ORM Mapper hier vor.

Magische Methoden und Interzeptoren mit PHP

F18 High Contrast
Attribution-NonCommercial-NoDerivs License by SkipSteuart
In PHP gibt es sogennante magische Methoden. Diese werden nicht explizit aufgerufen, sondern werden vom Interpreter in gewissen Situationen ausgeführt. Dazu gehören Konstruktor, Dekonstruktor. Die Implementierung magischer Methoden ist optional. Die Interzeptoren sind eine Untermenge der magischen Methoden. Mit diesen Methoden kann man bequem Execeptions oder Laufzeitfehler im objekt-orientierten Umfeld abfangen.

Beginnen möchte ich mir der Funktion __toString(), die beim Debuggen sehr nützlich ist. Dazu ein kleines Beispiel:

class Post{
	private $title ="", $text = "";


	public function __construct($title="", $text=""){
		$this->title =  $title;
		$this->text=$text;
	}
}
$post = new Post("mein 1. Eintrag",  "Schönes Wetter, spazieren gegangen, Eis gegessen.");
echo $post;

Der Befehl echo post liefert einen Fehler. Wenn man ein Objekt in PHP genauer betrachten möchte, muß man dies mit print_r() oder var_dump() tun – oder mit __toString():

	public function __toString(){
		ob_start();
		var_dump($this);
		$retval=ob_get_clean();
		return $retval;
	}

Da __toString() eine Zeichenkette zurückgibt, kann man alle String Funktionen von PHP anwenden:

class Post{
	private $title ="", $text = "";


	public function __construct($title="", $text=""){
		$this->title =  $title;
		$this->text=$text;
	}

	public function __toString(){
		return "

{$this->title}

{$this->text}"; } } $post = new Post("mein 1. Eintrag", "Schönes Wetter, spazieren gegangen, Eis gegessen."); echo str_replace("ö", "ö", $post);

Mit __set() und __get() kann man Zugriffe auf nicht gesetzte oder geschützte Attribute abfangen.

class Post{
        /**....**/
        public $foo="bar";
	public function __get($attrib){
		echo "$attrib gibts nicht, oder du hast keinen zugriff darauf!

"; } } $post = new Post("mein 1. Eintrag", "viel passiert heute."); echo $post->bla; echo $post->title; echo $post->foo;

Die Rückgabe sieht dann so aus:

bla gibts nicht, oder du hast keinen zugriff darauf!
title gibts nicht, oder du hast keinen zugriff darauf!

bar

Der Zugriff auf nicht definierte oder geschützte Methoden läßt sich mit __call abfangen:

class Post{
	public function __call($meth, $args){
		echo "Die Methode $meth gibts nicht, oder du hast keinen Zugriff darauf!

"; if (!empty($args)){ echo "Parameter:
"; foreach ($args as $arg) echo "$arg
"; } } } $post->leaveComment("karl heinz", "echt super", "karl@heinz.de");

Die Ausgabe sieht dann so aus:

Die Methode leaveComment gibts nicht, oder du hast keinen Zugriff darauf!
Parameter:
karl heinz
echt super
karl@heinz.de

Die beiden Interzeptoren __isset() und __unset() können entsprechende Anfragen an das Objekt abfangen:

class Post{
	public function __isset($value){
		return isset($this->$value);
	}
	public function __unset($value){
		if(isset($this->$value))
			unset ($this->$value);
	}

}
$post = new Post();
echo isset($post->title);
unset ($post->title);

Statt nur Fehler mit den Interzeptoren auszuwerfen, kann man Klassen schreiben, die zwar ein wenig „dirty“ sind aber auch funktionieren:

class DirtyPost{
	public  $attributes = array();

	public function __construct($title="", $text=""){
		$this->attributes['title'] =  $title;
		$this->attributes['text'] =  $text;

	}

	public function __toString(){
		$retval ="

{$this->attributes['title']}

"; $retval .=$this->attributes['text']; $retval .=$this->getComments(); return $retval; } public function __get($attrib){ if (isset($this->attributes[$attrib])) return $this->attributes[$attrib]; } public function __set($attrib, $value){ $this->attributes[$attrib] = $value; } public function __call($meth, $args){ switch ($meth){ case 'addComment': array_push ($this->attributes, $args); break; default: echo "$meth gibt es nicht."; case 'getComments': $retval =""; foreach ($this->attributes as $attrib){ if(is_array($attrib)){ $retval.= "Kommentar:
"; foreach ($attrib as $a) $retval.= $a.'

'; } }//each return $retval; break; }//switch }//__call } $post = new DirtyPost(); $post->title="Mein 1. Eintrag

"; $post->text = "Heute wars nicht so schoen

"; $post->addComment('Hans', 'hans@hansi.de', 'das schert mich nicht :)'); $post->addComment('Ingo', 'ingo@web.de', 'Morgen wirds besser'); echo $post;

Ausgabe:

Mein 1. Eintrag

Heute wars nicht so schoen
Kommentar:
Hans

hans@hansi.de

das schert mich nicht :)

Kommentar:
Ingo

ingo@web.de

Morgen wirds besser

Ausgelassen habe ich __callstatic mit dem man Aufrufe von nicht existierenden statischen Methoden abfangen kann.
Mit den Interzeptoren kann man sich viel Arbeit sparen, aber auch viel Verwirrung stiften. Das letzte Beispiel funktioniert zwar (wenn man serialize() und unserialize() noch einbindet, hat man einen minimalistischen Blog) allerdings leiden Lesbarkeit, Wiederverwendbarkeit darunter.

Fehlerbehandlung mit Singleton Pattern in PHP

Anonymous Single Malt Scotch
Attribution-NonCommercial-ShareAlike License by clappstar

Heute gibt’s mal wieder ein nettes Erzeugungsmuster: das Singleton oder Einzelstück. Mit diesem Pattern kann man verhindern, dass mehr als ein Objekt einer Klasse instanziiert wird. Nützlich ist das Pattern für die Kapselung von Datenbankverbindungen, oder wie in meinem Fall zur Fehlerbehandlung.

Das Prinzip ist schnell erklärt:

Da wir keine neuen Objekt der Klasse erzeugen wollen, ist der Konstruktor uninteressant. Alles was benötigt wird ist eine Referenz auf das einzige erzeugte Objekt. Dafür gibt es die statische Methode getInstance(). In der Methode wird geprüft, ob schon ein Objekt der Klasse vorhanden ist. Falls nicht, wird eine Instanz erzeugt und die Referenz im Klassenattribut $uniqueInstance gespeichert In jedem Fall wird das statische Attribut $uniqueInstance zurückgegeben. .
Der Beispielcode:

class ErrorManager {
	private  $errors, $errorCodes;
	private static $uniqueInstance=NULL;

	private final function __clone(){}
	private function __construct(){
		/** **/
	}

	public static function getInstance(){
		if (self::$uniqueInstance === NULL)
			self::$uniqueInstance = new ErrorManager();
		return self::$uniqueInstance;
	}//getInstance
}

Copy Operator und Konstruktor werden überschrieben, damit niemand durch Vererbung das Pattern aushebeln kann. Die Referenz auf das Objekt können wir uns mit dem Scope Operator besorgen:

$err_manager = ErrorManager::getInstance();

Wer es nicht glaubt kann die Instanz 2 verschiedenen Variablen zuweisen und vergleichen:

$e1 = ErrorManager::getInstance();
$e2 = ErrorManager::getInstance();
if ($e1 === $e2)
    echo "equal";

Soviel zum Singleton Pattern und weiter mit der Fehlerbehandlung. Fehlermeldungen sind immer ein zweischneidiges Schwert, wenn es um Webapplikationen geht. Dem Entwickler helfen sie und irgendwelchen Scriptkiddies leider auch. In der php.ini kann man sie zwar komplett abschalten, aber vielleicht möchte man die Meldungen bestimmten angemeldeten Nutzern nicht vorenthalten. Meine Interpretation des Errorhandlings ist durchaus ausbaufähig aber schon recht brauchbar. Wichtig für die Funktionalität ist das Hochschrauben des Error Reportings:

error_reporting( -1 );

Nun muss ein Errorhandler angegeben werden . Das mache ich im Konstruktor:

	private function __construct(){
			$this->errors = array();
			error_reporting( -1 );
			set_error_handler ( array(__CLASS__, 'errorHandle' ) );
	}

Statt Warnungen und Fehler in die Ausgabe zu schreiben, wird alles nun an die Methode errorHandle übergeben. Ich habe ein Array mit abstrakten Datentypen – AdtError genannt -angelegt. Die Methode erzeugt nun AdtError Objekte und pusht diese in ein Array:

	public static function errorHandle( $code, $msg, $file, $line ){
		$error = new AdtError($code, $msg, $file, $line);
		$s = self::getInstance();
		array_push($s->errors, $error);
	}//errorHandle

Für die Ausgabe hat AdtError die Java-like Methode toString():

	public function getErrors(){
		$s = self::getInstance();
		foreach ($s->errors as $error)
			echo $error->toString().'

';

	}//getErrors

Wenn man nun den Errorhandler anmeldet und ein wenig Unfug schreibt, wird alles wunderbar abgefangen und bei Bedarf ausgegeben:

$err_manager = ErrorManager::getInstance();

foreach ($e_codes as $code => $value)
	echo $code ." ". $value."

";

$err_manager->getErrors();

Bildschirmausgabe:

E_NOTICE Undefined variable: e_codes /home/schmiddi/web/gloria/err_handling/CErrorhandling.php 93
E_WARNING Invalid argument supplied for foreach() /home/schmiddi/web/gloria/err_handling/CErrorhandling.php 93

Wunderbar. Wie schon gesagt gibts durchaus Raum für Verbesserungen. So könnte man der Klasse Abbruchbedingungen beibringen, optional in Textdateien schreiben, schöner Formatieren das Ding in ein MVC Muster hauen usw. Für Interessierte gibts noch den Quelltext: ErrorManager

Abstract Factory mit PHP

Battersea Power Station from Train, 29-04-06
Attribution-NonCommercial License by DG Jones

Inzwischen beschäftige ich mich fast nur noch mit PHP und aus Skripten wird langsam Programmieren. Ich möchte hier mit einem tatsächlich im RL vorkommendes Beispiel zeigen, dass Design Patterns nicht nur elegant und oo. sind sondern auch bei kleineren Skripts sehr nützlich sind.

Vor einigen Wochen habe ich mein unsägliches WordPress Plugin geschrieben. Dort werden unter anderem auch Urls mit verschiedenen Diensten verkürzt. Quick ’n Dirty habe ich eine Klasse angelegt und Methoden wie getShortUrlIsgd(), getShortUrlTinyUrl() erzeugt. Am Anfang gings noch relativ gut, bis ich Anfragen für andere Services bekam. Die wurden dann nach gleichem Schema in die Klasse gepastet. Die Auswahl des Urlshortener verläuft dann auch entsprechend unelegant über ein Switch Case Statement:

switch ($selected_shortener){
	case 'is.gd':
		$new_url=$this->getShortUrlIsgd();
		break;
	case 'bit.ly':
		$new_url=$this->getShortUrlBitly(	  get_option('schmie_tw_bitly_usr'),
					get_option('schmie_tw_bitly_api'));
		break;
	case 'i2h.de':
		$new_url=$this->getShortUrlI2h();
		break;
	case 'tinyurl.com':
		$new_url=$this->;getShortUrlTinyurl();
		break;
	}//switch

Mit einem Erzeugungsmuster wird die Geschichte wesentlich eleganter. Ich habe mich für die „Abstrakte Fabrik“ / Abstract Factory Kit entschieden. dabei wird eine abstrakte Klasse geschrieben, die zur Laufzeit die tatsächlich zu verwendende Klasse einbindet. Zur Übersichtlichkeit habe ich auf include und require Mechanismen in dem Beispiel verzichtet.

Betrachten wir zunächst die Factory Klasse:

abstract class ShortenerInterface{
	//some attributes
	protected  $login, $apikey,$serviceurl,$longurl;

	protected function __construct($login="", $apikey=""){
		$this->login = $login;
		$this->apikey = $apikey;
	}

	public static function factory($class,$login="", $apikey=""){
		return new $class($login=$login, $apikey=$apikey);

	}//factory

	protected function get_url($longurl) {
	/** ... **/
	}//get_url

       //diese Funktion will in jeder abgeleiteten Klasse implementiert werden
	public abstract function get_short_url($url);

}//class

Der Konstruktor ist relativ egal für das Designprinzip. Außer Attributzuweisungen macht er nicht viel. Wesentlich interessanter ist die Methode factory. Durch das Schlüsselwort static kann die Methode aufgerufen werden, ohne vorher das Objekt zu instanziieren (geht auch schlecht bei abstrakten Klassen).

Zurückgegeben wird eine Instanz der Gewünschten Klasse. Die beiden Argumente werden einfach durchgereicht. Die Abstrakte Methode get_url($longurl) muß in jeder abgeleiteten Klasse implementiert werden.

Ein Beispiel:

class CTinyurl extends ShortenerInterface{
	public function get_short_url($longurl){
		$this->serviceurl='http://tinyurl.com/api-create.php?url=';
		return $this->get_url($longurl);
	}//function
}//class

Der Implementierungsaufwand ist minimal wie man sieht. An eine Konkrete Instanz kommt man so:

$iface = ShortenerInterface::factory("CTinyurl");
$iface->get_short_url("http://schmiddi.co.cc");

oder so:

$services = array('CIsgd', 'CTinyurl', 'CI2h', 'CBitly');
foreach ($services as $service){
	$iface = ShortenerInterface::factory($service,   "", "");
	$url =  $iface->get_short_url("schmiddi.co.cc");
	echo "$url
";

Seehr schön. Auch wenn man funktional (wie in meinem Plugin-Fall) schreiben muß, sollte man immer mal kurz darüber Nachdenken etwas aus der Softwaretechnik Vorlesung zu implementieren. Über Verbesserungen und Vorschläge würde ich mich wie immer sehr freuen.

Wer den Code haben will: factory

P.S.:Wer das Kraftwerk auf dem Bild erkennt hat einen wirklich guten Musikgeschmack.

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.

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. 🙂

Notizen zur Pluginschreiberei für WordPress

spot the wordpress
Attribution-NonCommercial-ShareAlike License by miss604

Wordpressplugins selbst schreiben ist kein Problem. Die Dokumentation ist recht ordentlich, manche Dinge sind jedoch ein wenig hakelig. Aber mit ein wenig Hartnäckigkeit erzielt man rasch Ergebnisse.

Einen englischsprachigen Einstieg findet man hier:

Diese Anleitung orientiert sich an dem Plugin schmie_twitter. Download kann man es hier. Die Eigentlichen Funktionen sind soweit es geht von WordPresscode getrennt.

Das Plugin aktualisiert den Status bei Shortmessaging Diensten (Twitter und Identica), wenn ein neuer Artikel erstellt wurde, oder ein älterer aktualisiert wurde.

Näher betrachtet wird folgendes:

  • Das Erstellen einer Settingspage.
  • Lesen und Schreiben von WordPress Einstellungen
  • Das Hook- oder Action System von WordPress
  • Übersetzungen von Plugins
  • Verteilen eines Plugins über das Plugin Directory

Ich möchte an dieser Stelle keine deutsche Übersetzung der WordPressdokumentation erstellen, sondern kurz und bündig beschreiben, was wie funktioniert.

Dieses Plugin sendet keine Ausgaben an den Benutzer, abgesehen von einer Seite mit Einstellungen.

in guter Einstieg ist das Betrachten der Verzeichnisshierarchie

schmiddi@dasgrauen:~/web/schmie_twitter$ ls -R

.:

classes  img  lang  readme.txt  schmie_twitter_options.php  schmie_twitter.php  screenshot-1.png  uninstall.php

./classes:

Cschmie_twitter.php  CToIdentica.php  CTo_Twitter.php  CUrlshortener.php

./img:

logo.png

./lang:

schmie_twitter-de_DE.mo  schmie_twitter.de_DE.mo  schmie_twitter.po

Die beiden letzten Ordner sind relativ unwichtig. In img. liegt eine Bilddatei mit dem Logo und in lang sind die zur Internationalisierung nötigen Dateien abgelegt. Dazu später mehr.

In classes liegen 4 Klassen, die die Grundfuntionen bereitstellen.

schmie_twitter.php ist der Einstieg in das Plugin.

Um ein Plugin zu schreiben wird nur eine PHP Datei benötigt, die den Namen des Plugins trägt. Die Datei kann auch in einen Unterordner nach diesem Schema abgelegt werden : schmie_twitter/schmie_twitter.php

Wichtig ist die Einzigartigkeit des Namens. Leider bietet WordPress keine Namespacefunktionen oder sonstige Garantien der Einzigartigkeit. Ich wähle zur Namensvergebung einen Präfix und anschließend einen prägnanten Namen. Die Plugindatei benötigt einen speziellen Header, der von WordPress geparsed wird:

Actions & Hooks

Zumindest der Plugin Name muß gesetzt sein, damit WordPress das Plugin akzeptiert.

Was nun folgt ist normaler PHP Code:

	//Codeauszug schmie_twitter.php

	function post_twit($post_ID) {
	/**
	....
	**/
	}//function
	add_action('publish_post','post_twit');

Bemerkenswert ist der add_action Mechanismus. (http://codex.wordpress.org/Function_Reference/add_action)

Um dies zu erklären, muß auf die Hook Mechanismen eingegangen werden. Bestimmten Ereignisse sind mit Hooks (Haken) oder actions (Aktionen) verbunden. Wenn diese Ereignisse ablaufen, wird abgeprüft, welche Funktionen noch ausgeführt werden sollen. Diese werden mit der add_action Funktion angemeldet. In unserem Fall wird bei dem Ereigniss ‚publish_post‘ also bei der Veröffentlichung eines Posts die Funktion post_twit angehängt und entsprechend ausgeführt.

Ein weiteres Beispiel:

/**** Save Settings **/
	$st= new CSchmie_twitter();
	add_action( 'admin_init', 'register_mysettings' );
	function register_mysettings() {
		global $st;
		$st->register_mysettings();
	}//function

Hier wird deutlich, dass das Actionsystem nur Funktional und nicht objekt-orientiert angesteuert werden kann. In der Funktion register_mysettings wird die Methode register_mysettings des CSchmie_twitter Objekts aufgerufen. Das Schlüsselword global wird benötigt um die Variable innerhalb der Funktion bekannt zu machen, da PHP merwürdige Gültigkeitsbereiche im Vergleich zu anderen Sprachen hat.

Ein letztes Beispiel wäre das Erzeugen einer Settingspage:

/** @see schmie_twitter_options.php ***/
add_action('admin_menu', 'schmie_twitter_create_menu');
function schmie_twitter_create_menu() {
	add_options_page('schmie_Twitter Settings', 'schmie_twitter Settings', 'administrator', __FILE__, 		'schmie_twitter_settings_page', __FILE__);

}//function

function schmie_twitter_settings_page() {
	if ( file_exists ( dirname(__FILE__).'/schmie_twitter_options.php' )) {
		include( dirname(__FILE__).'/schmie_twitter_options.php' );
	} else {
		e( '

Couldn't locate the settings page.

', 'wp-to-twitter' );
	}//fi
}//function
Auch hier werden wieder Grenzen des Systems aufgezeigt. Zuerst wird die Action create_menu() bei der Generierung des Administratormenüs aufgerufen und erzeugt die Links zu der Einstellungsseite und anschließend wird die Einstellungsseite generiert. Der include Mechanismus ist analog zum Präprozessorbefehl in C.
Durch die Modularisierung mit Hooks kann dies nicht in einem Schritt geschehen. Möglich wäre nur eine Zusammenfassung innerhalb einer neuen Funktion . (@@@wirklch? Meta Hooks?)

Optionspages

Der nächste Punkt ist das Erstellen einer Einstellungsseite (Optionspage).
Zum größten Teil werden hier nur Abgleichungen mit der Datenbank erledigt.
Wichtig hierbei sind die Funktionen register_settings(‚optiongroup‘,’option‘), und get_option(‚option‘).
WordPress speichert sämtliche Einstellungen in einer Datenbanktabelle namens Options. Die Funktion register_settings prüft das Vorhandensein der Einstellung und legt sie gegebenfalls an. Bei Optiongroup handelt es sich um eine Art Namespace, um die Einzigartigkeit zu gewährleisten.
Die Verarbeitung der Einstellungen geschieht über ein HTML Formular. Zuerst wird die Optiongroup bekannt gemacht:
<?php settings_fields( ’schmie_settings_group‘ ); ?>
Der Lese / Schreibzugriff wird über die Funktion get_option(‚option‘) abgewickelt. Die Funktion ist robust, doch leider mit sehr viel HTML verbunden:
BESCHREIBUNG
<input name="OPTION NAME" type="text" value=";" />

Zum einen ergbit dies eine sehr unelegante Vermischung aus PHP un HTML, zum anderen ist es mit einigem Schreibaufwand verbunden. Generatorfunktionen sind meines Wissens nicht vorhanden. Sehr nützlich für mich waren ein paar einfache PHP Funktionen, die die Einstellungen mit einem HTML Rumpf ausgeben:

function schmie_generate_textfield($description,$option,$password=false) {
	$option_val=get_option($option);
	$type='text';
	if ($password)
		$type='password';

echo "	
        $description
        
        ";
}//function

//looks nice :)
schmie_generate_textfield(__("Twitter Password", 'schmie_twitter'), 'schmie_tw_pass',true);

Bei der Namenswahl sollte man immer recht vorsichtig sein, damit keine WordPressfunktionen überschrieben werdem. Auch hier hat sich wie bei Settings das Präfixsystem für mich bewährt.

Übersetzung

Das Thema Internationalisierung sollte direkt zu Anfang der Entwicklung betrachtet werden. Dies erspart viel Änderungen am Code. Verwendet wird die innerhalb der Sprache PHP implementierte Gettext Bibliothek. Das Softwarepaket gettext ist für Übersetzungen erforderlich.

Links:
http://www.gnu.org/software/gettext/manual/gettext.html
http://www.php.net/manual/de/intro.gettext.php
http://codex.wordpress.org/I18n_for_WordPress_Developers

Sehr empfehlenswert ist dieser Beitrag aus der Blogosphäre, der deutlich tiefer auf die Thematik eingeht. Leider habe ich den Artikel zu spät entdeckt:(:

http://www.tuxlog.de/wordpress/2007/wordpress-plugins-ubersetzen-mit-gettext-po-und-pot-dateien/

Zuerst muß im Code gekennzeichnet werden, was übersetzt werden soll. Dabei wird die Zeichenkette mit einem Funktionsaufruf umschlossen und ein Namespace für die Übersetzung angegeben:

$foo=__('Dies könnte Ihre Übersetzung sein', 'NAMESPACE') .

Eine direkte Ausgabe erfolgt mit :

_e('Hallo Welt', 'NAMESPACE');

Platzhalter sind auch möglich:

_e("$ctr Flaschen Bier an der Wand",'NAMESPACE');

oder mit

printf(__("%d Flaschen Bier an der Wand",'NAMESPACE'), $ctr);

Das Einsetzten des Namespaces kann auch durch diverse Skripte (z.B.: innerhalb der Admin Funktionen im WordPress Plugindirectory). Dann genügt auch ein $bar = __(‚Hallo W‘);

Nun wird mit xgettext mit den o.g. Admin Funktionen eine POT Datei (Portable Object Template) erzeugt.

xgettext -L PHP -k –keyword=_e –keyword=__ –from-code=utf8 –default-domain=schmie_twitter –output=schmie_twitter.pot *.php

Dabei handelt es sich um eine Liste der zu übersetzenden Strings. Nun erstellt man eine Kopie der POT Datei und pflegt die entsprechenden Übersetzungen dort ein:

cp schmie_twitter.pot schmie_twitter-de_DE.po

Dies kann per Texteditor oder speziellen Tools wie KBabel oder Poedit erfolgen.

Wenn dies erledigt ist, wird eine binäre Datei mit der Namenskonvention pluginname-<lang_string>.mo mit msgfmt erzeugt:
msgfmt -o schmie_twitter.mo de_DE.po
Den Namnesstring erfährt man in der wp-config.php. Die Datei ist im Hauptverzeichniss der WordPress installation zu finden. Der String für deutschsprachige Unterstützung sieht so aus: define (‚WPLANG‘, ‚de_DE‘);

Nun muß die Übersetzung innerhalb des PHP Codes geladen werden. Dies geschieht mit folgenden Zeilen:

$path= basename(dirname(__FILE__));
	$domain='schmie_twitter';
	load_plugin_textdomain('schmie_twitter', false, $path . '/lang');
Ich habe mich dazu entschlossen alle Übersetzungen in den Unterordner lang zu legen.

Deinstallation:

Ein Skript kann händisch entfernt werden, indem einfach alle PHP Dateien entfernt werden. Datenbankeinträge bleiben dabei jedoch vorhanden. Deswegen solle immer der Deinstallationsmechanismus von WordPress verwendet werden.
Um eine Uninstallroutine zu schreiben, gibt es 2 Möglichkeiten:
-per hook
-Uninstall.php
Bei der Methode mit Hook wird wie bei den oben beschrieben Actions vorgegangen:
register_uninstall_hook(__FILE__, 'my_uninstall_hook');
	function my_uninstall_hook() {
		delete_option('option_1');
	}//
Allerdings wurde dieser Hook erst mit der Version 2.7 von WordPress implementiert. Man kann zwar mit function_exists(callback) eine Fehlermeldung abfangen, allerdings passiert bleiben die Einstellungen in der Datenbank.
Deshalb sollte man dem Weg mit der Uninstall.php den Vorzug geben. Dabei wird im Hauptverzeichnis des Plugins die genannte Datei erzeugt. Bevor man an das entfernen der Einstellungen geht, sollte man Prüfen, ob die WP_UNINSTALL Konstante gesetzt ist:

	if(!defined('WP_UNINSTALL_PLUGIN') )
	    exit();
	delete_option( 'schmie_tw_user' );

das WordPress Plugin Directory

Da man Plugins aus dem Directory recht bequem direkt in WordPress installieren kann, empfiehlt es sich seine Werke dort zu veröffentlichen. Nach der Registrierung (Passwort aufschreiben) füllt man einen Antrag auf Aufnahme des Plugins an. Ein WordPress Mensch schaut sich das Plugin an und nach einer gewissen Zeit (Spanne: Stunden bis Tage) wird das Plugin freigeschaltet. Zuerst schaut man sich die Pluginseite an, vor allem den Adminbereich. Was jetzt noch fehlt ist die Beschreibung im Directory. Dies geschieht über ein Textfile. Ausreichende Infos gibts hier.
Es gibt auch eine sehr brauchbare Seite mit Informationen zum Repo http://wordpress.org/extend/plugins/about/svn/.

Hier die Schnellversion (auch für den Laien):
Die Adresse des Repo’s findet Ihr auf Eurer Pluginseite.
Zuerst wird in ein leeres Verzeichniss ausgecheckt (Check Out):

	svn co http://plugins.svn.wordpress.org/schmie-twitter/

Hier gibts 3 Ordner branches tags trunk. Die aktuelle Version des Plugins liegt immer im trunk.
Also das Plugin dorthin kopieren. Nun fügen wir die Dateien dem Repository hinzu:

svn add trunk/*

Sehr nett ist es ältere Versionen im Tagsverzeichnis zu sichern. Dann stehen auch diese Versionen zum Download zur Verfügung. Falls man mal in älteren Code sehen will und nicht so bewandert mit SVN ist, hat man hier alles schnell zur Hand also:

svn cp trunk tags/1.0

Eingecheckt wird so (Check In):

svn ci -m "hey du willst es hier ist es"

Anmeldung am Repository mit den Directory Benutzerdaten.
Jetzt noch ein paar Worte für diejenigen, die noch nie mit Versionierungssoftware gearbeitet haben. Löschen im Dateimanager ist recht sinnlos, wenn man mit SVN oder ähnlichem arbeitet, da die Datei(en) zwar von der Platte getilgt sind, aber nicht aus dem Repo. Das gilt auch für Verschieben oder Umbenennen von Dateien. Auf solche Details ist immer zu achten. Eine gute Dokumentation für Subversion findet sich in Version Control with Subversion.

Das wärs von mir zu diesem Zeitpunkt. Viel Freude beim skripten / programmieren. Für Tipps oder Verbesserungen bin ich wie immer sehr dankbar. Wenn ich mal wieder ein Widget schreibe oder eines besseren belehrt werde, wird der Artikel angepasst.

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 :).