<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Yoda Condition &#187; HowTo</title>
	<atom:link href="http://yoda.neun12.de/artikel-category/wordpress/howto/feed" rel="self" type="application/rss+xml" />
	<link>http://yoda.neun12.de</link>
	<description>Debuggen du musst</description>
	<lastBuildDate>Sun, 18 May 2014 13:49:01 +0000</lastBuildDate>
	<language>de-DE</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>TinyMCE zur Bearbeitung von Mediendateien</title>
		<link>http://yoda.neun12.de/artikel-143</link>
		<comments>http://yoda.neun12.de/artikel-143#comments</comments>
		<pubDate>Sun, 18 May 2014 13:40:02 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=143</guid>
		<description><![CDATA[Alle sagen es geht nicht bis einer kommt und es einfach macht. So ging es mir bei diesem G+ Post. Das Problem ist einfach umrissen: In der Medienübersicht wird der Standard-Editor zur Bearbeitung der Medienbeschreibung verwendet. Der Wunsch war jedoch den TinyMCE dafür einzusetzen. Normalerweise reicht es dafür aus einen Filter auf die Editor-Einstellungen zu [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Alle sagen es geht nicht bis einer kommt und es einfach macht. So ging es mir bei <a title="Google+" href="https://plus.google.com/u/0/+ReneWelz/posts/H4RMbXhaVb2">diesem G+ Post</a>. Das Problem ist einfach umrissen: In der Medienübersicht wird der Standard-Editor zur Bearbeitung der Medienbeschreibung verwendet. Der Wunsch war jedoch den TinyMCE dafür einzusetzen. Normalerweise reicht es dafür aus einen Filter auf die Editor-Einstellungen zu verwenden und das Flag für den TinyMCE auf <code>True</code> zu setzen.</p>
<p>Wir haben im Prinzip drei Möglichkeiten um die Editor-Einstellungen zu beeinflussen. Zum einen dann wenn sie definiert werden.</p>
<pre class="brush:php">$editor_args = array(
	'textarea_name' =&gt; 'content',
	'textarea_rows' =&gt; 5,
	'media_buttons' =&gt; false,
	'tinymce' =&gt; false,
	'quicktags' =&gt; $quicktags_settings,
);

$editor_args = apply_filter( 'some_filter', $editor_args );
</pre>
<p>Leider ist dies nicht immer möglich, vor allen Dingen im gegebenen Fall nicht. Leider bietet auch die verwendetet Funktion <code>wp_editor()</code> noch die die Klasse <code>_WP_Editors</code> einen entsprechenden Filter an. Warum das so ist, sei mal dahingestellt. Ebenso das es bereits diverse Tickets im Trac zu den Thema gibt. Wenn man sein Ziel nicht auf direkten Weg erreichen kann, dann muss man manchmal einen kleinen Umweg gehen.</p>
<p>Eine Lösung wäre, wenn die Funktion <code>wp_editor()</code> plugable wäre, man sie also gegen eine eigene Funktion austauschen kann. Ist sie aber nicht, muss man den Hebel also woanders ansetzen.</p>
<p>Im betreffenden Fall wurde angeregt in der <a title="Trac Ticket #28259" href="https://core.trac.wordpress.org/ticket/28259">Klasse einen zusätzlichen Filter</a> einzubauen. Bis dies umgesetzt ist, muss man sich also mit einen kleinen Workaround behelfen.<br />
Wirft man einen Blick in den <a title="Core WordPress Browse Trunk" href="https://core.trac.wordpress.org/browser/trunk/src/wp-includes/general-template.php#L2280">Code</a>, so sieht man das abgefragt wird ob die Klasse <code>_WP_Editors</code> bereits geladen wurde. Nur wenn sie <em>nicht</em> geladen wurde, wird die Klasse aus dem WordPress-Core geladen. Für uns bedeutet dies also, wir können die Original-Klasse durch eine gleichnamige eigene Klasse ersetzen. Wir müssen nur dafür sorgen das unsere Klasse möglichst früh geladen wird. Am einfachsten erreicht man dies indem man einen der sehr früh ausgelösten Hooks benutzt um die eigene Klasse mittels <code>require_once()</code>/<code>include_once()</code> einzubinden.</p>
<p>Wie man sieht, gibt es in WordPress nicht nur eine Möglichkeit um etwas zu beeinflussen. Der einfachste Weg sind Filter und Actions um Variablen direkt zu beeinflussen. Sind diese nicht gegeben, schaut man sich an ob die betreffende Funktion etwas hergibt, also ob diese ggf. einen Filter/Action besitzt oder ob man sie ersetzen kann. Ist dies auch nicht gegeben, so kann man, wie hier im Beispiel, noch eine letzte Chance nutzen und eine Klasse komplett austauschen.</p>
<p>Der erste Schritt zum Plugin besteht also darin die Klasse <code>_WP_Editors</code> komplett in sein Plugin-Ordner zu kopieren. Der nächste Schritt ist das Einbinden der kopierten Klasse:</p>
<pre class="brush:php">/**
 * Including the modified WP class _WP_Editors
 */
add_action(
	'plugins_loaded',
	function() {
		require_once dirname( __FILE__ ) . '/class-wp-editor.php';
	},
	0,
	0
);
</pre>
<p>Um zu überprüfen ob nun die kopierte oder die Original-Klasse geladen wird, kann man am Anfang der Kopie einfach mal ein <code>die( 'Kopierte Klasse' );</code> setzen. Bricht WordPress nach einem Neuladen mit einen weißen Bildschirm ab auf dem nur &#8220;<em>Kopierte Klasse</em>&#8221; steht, so können wir sicher sein das nur unsere kopierte Klasse geladen wird. Nun geht es daran die kopierte Klasse zu modifizieren. Wie im Ticket vorgeschlagen ergänzen wir die Klasse um einen zusätzlichen Filter.</p>
<p>Da wir nun in der kopierten Klasse einen Filter haben den wir nutzen können, reicht es aus diesen zu setzen und in einer passenden Funktion die Werte entsprechend zu ändern.</p>
<pre class="brush:php">/**
 * Add the filter to modify the editor settings
 */

add_filter( 'wp_editor_settings', 'modify_editor_settings', 0, 2 );

/**
 * Modify the editor settings. Currently only set the value for using the TinyMCE to true.
 *
 * @param array $settings Array with editor settings
 * @param array $editor_id The ID of the editor
 * @return array
 */
function modify_editor_settings( $settings, $editor_id ) {

	if ( 'attachment_content' == $editor_id )
		$settings['tinymce'] = true;

	return $settings;

}
</pre>
<p>Ich habe hier bewusst auf umständliche Prüfungen verzichtet um das Beispiel einfach zu halten. Man könnte zusätzlich darauf prüfen ob man sich auch tatsächlich in der Medienübersicht bzw. Medienbearbeitung befindet. Es wird lediglich geprüft ob die Editor-ID <code>attachment_content</code> ist und dann das Flag für den TinyMCE auf <code>true</code> gesetzt.<br />
Durch Anpassung der Funktion <code>modify_editor_settings()</code> lässt sich so der TinyMCE für nahezu alle Eingabefelder aktivieren sofern die Editor-ID bekannt ist und das Eingabefeld über die Funktion <code>wp_editor()</code> erzeugt wird.</p>
<p>Das Plugin ist recht wartungsfreundlich. Sollte eines Tages ein Filter zum Modifizieren der Editor-Einstellungen vorhanden sein, so muss man nicht das komplette Plugin neu konstruieren. Es reicht aus die Einbindung der eigenen Klasse zu entfernen und ggf. den Filter-Namen anzupassen.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-143/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress in einer Nussschale</title>
		<link>http://yoda.neun12.de/artikel-131</link>
		<comments>http://yoda.neun12.de/artikel-131#comments</comments>
		<pubDate>Thu, 26 Dec 2013 01:56:20 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=131</guid>
		<description><![CDATA[Es gibt verschiedene Gründe warum man WordPress in einer Sandbox installieren möchte, ich habe nun einen weiteren Weg entdeckt den ich hier vorstellen möchte. Mit &#8220;Sandbox&#8221; meine ich hierbei, eine relativ abgeschlossene Umgebung die alles benötigte (Server, PHP, MySQL) mitbringt. Sandboxes Eine Möglichkeit ist Instant WordPress, welches auf Apache, PHP und MySQL setzt. Mit diesen [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Es gibt verschiedene Gründe warum man WordPress in einer Sandbox installieren möchte, ich habe nun einen weiteren Weg entdeckt den ich hier vorstellen möchte. Mit &#8220;Sandbox&#8221; meine ich hierbei, eine relativ abgeschlossene Umgebung die alles benötigte (Server, PHP, MySQL) mitbringt.</p>
<h3>Sandboxes</h3>
<p>Eine Möglichkeit ist <a title="Instant WordPress" href="http://www.instantwp.com/" target="_blank">Instant WordPress</a>, welches auf Apache, PHP und MySQL setzt. Mit diesen Programm lässt sich WordPress inkl. der nötigen Umgebung z.B. auf einen USB-Stick installieren. Soweit eine tolle Sache, jedoch kann man die installierte Umgebung nur schlecht bis gar nicht anpassen. WordPress liegt i.d.R. in einer sehr alten Version bei, so dass man dies erst einmal updaten darf. Ob man PHP und MySQL überhaupt updaten kann, damit habe ich mich schon gar nicht mehr beschäftigt. Das Paket selber kann man ohne Fachwissen nicht anpassen, man muss nehmen was der Entwickler einen liefert.<br />
So toll Instant WordPress auf den ersten Blick ist, für mein Problem, das mich aktuell beschäftigt, ist es nicht das richtige. Schnell fiel mir wieder <a title="nginx webserver" href="http://nginx.org/" target="_blank">Nginx</a> ein, ein Webserver der vor einiger Zeit wegen seiner hohen Geschwindigkeit für viel Aufsehen sorgte. Für mich war Nginx eher deshalb interessant, da man ihn unabhängig von anderen, bereits installierten Komponenten, verwenden kann. Es wäre also möglich sich seine eigene Sandbox mit Nginx, PHP und MySQL zu basteln, was ich auch versucht habe. Jedoch ist das Frustlevel recht schnell sehr hoch. Denn im Netz findet man häufig nur Anleitungen zu Nginx 0.8.x, die aktuelle Version (Stand zur Veröffentlichung des Artikels) ist jedoch schon 1.5.8. Wobei &#8220;Anleitung&#8221; häufig geschmeichelt ist. Viele &#8220;Anleitungen&#8221; setzen enormes Wissen voraus und verschweigen das meiste. Irgendwann habe ich es dennoch geschafft Nginx mit PHP zum laufen zu bekommen, hatte dann aber Probleme mit dem von XAMPP installierten MySQL.</p>
<h3>WT-NMP</h3>
<p>Eine weitere Suche im Netz förderte dann <a title="WT-NMP - portable Nginx Mysql Php development stack for Windows" href="http://wtriple.com/wtnmp/" target="_blank">WT-NMP</a> zu Tage. WT-NMP kann man zwischen XAMPP und Instant WordPress ansiedeln. Das Paket liegt u.a. als Zip-Archive vor und kann sowohl als Verzeichnis auf der Festplatte als auch auf einen portablen Medium wie z.B. einen USB-Stick installiert werden. Der große Vorteil an dem Paket ist, es konfiguriert sich selbst, man muss also nicht händisch viel anpassen damit man es zum Laufen bekommt. Der dickste Bonuspunkt ist, wenn man das Paket z.B. auf der Festplatte installiert und eingerichtet hat und anschließend auf einen USB-Stick kopiert, ändert es automatisch die Konfiguration so um, dass es auch auf dem USB-Stick weiterhin läuft.<br />
Ein weiterer Bonuspunkt ist das Fehlen von WordPress. Hört sich merkwürdig an, jedoch kann man sich so selber aussuchen welche Version oder gar welche Software man mit dem Paket bündelt. Da ich das Zip-Archiv und nicht den WT-NMP Installer verwende, kann ich ohne große Probleme alles ins Zip-Archiv schieben was ich brauche, sogar eine Auswahl an Plugins die ich immer wieder einsetze. Im Ergebnis habe ich dann ein Zip-Archiv das meinen Bedürfnissen entspricht, das ich selber Up-To-Date halten kann und an dem ich nicht jedes mal rumfummeln muss wenn ich eine neue WordPress-Sandbox benötige.<br />
Nach der Installation schlägt das Herz dann sogar noch einen Ticken höher, denn schlauerweise sind neben Nginx, MySQL und PHP noch HeidiSQL und Adminer zur Verwaltung der Datenbanken installiert. Obendrein gibt es noch einen RegExp-Tester für PHP (PCRE) und JavaScript. Das man das PHP Error-Log mit einen Mausklick aufrufen kann, Testmails verschicken und <a title="Xdebug - Debugger and Profiler Tool for PHP" href="http://xdebug.org/" target="_blank">XDebug</a>, <a title="xCache" href="http://xcache.lighttpd.net/" target="_blank">PHP XCache</a> und <a title="PHP: OPcache - Manual " href="http://php.net/opcache" target="_blank">PHP OPcache</a> vorinstalliert sind, macht die Sache dann komplett rund. Alles in allem ein Traum für alle die WordPress in einer Sandbox betreiben wollen.</p>
<h3>Wie geht&#8217;s?</h3>
<p>Ich möchte nun im folgenden kurz beschreiben wie man zum einen das Paket für den Betrieb mit WordPress bereit macht und wie man WordPress damit zum Laufen bekommt. Und keine Angst, mehr als einen Texteditor und 5-10 Minuten Zeit braucht es nicht. Als erstes brauchen wir natürlich WT-NMP und WordPress.</p>
<ul>
<li><a title="WT-NMP 13.12 Zip-Archive" href="http://sourceforge.net/projects/wtnmp/files/wt-nmp-13/wt-nmp-13.12.zip/download" target="_blank">WT-NMP 13.12 als Zip-Archive</a></li>
<li><a title="WP3.8 de-Edition" href="http://de.wordpress.org/wordpress-3.8-de_DE.zip" target="_blank">WordPress 3.8 (deutsche Version)</a></li>
</ul>
<p>Es gibt zwar neben dem Zip-Archive auch noch einen Windows-Installer, den kann man jedoch nicht so bequem anpassen wie das Zip-Archive. Nach dem Download entpackt man das Zip-Archive in ein Verzeichnis, bei mir ist es das Laufwerk <code>D:/</code> und als Verzeichnis <code>WT-NMP</code>. Wenn ihr es woanders hin entpackt, kein Problem. Das Laufwerk ist eh egal, der Verzeichnisname sollte zum besseren Verständnis jedoch <code>WT-NMP</code> lauten.<br />
Im Verzeichnis <code>WT-NMP</code> können wir den Unterordner <code>WWW</code> sehen. Man kann es sich fast denken, dass ist der Document Root, also das Verzeichnis in dem alle Web-Projekte landen werden. Dorthin entpacken wir nun WordPress ins Verzeichnis <code>wordpress</code> (man kann natürlich auch einen anderen Namen wählen<code></code>). Die Verzeichnisse <code>project1</code>, <code>project2</code> und <code>project3</code> kann man löschen. Die letzten beiden genannten sind ohnehin leere Verzeichnisse, in <code>project1</code> ist lediglich eine Testdatei die uns hier jedoch nicht weiter interessieren soll.<br />
An dieser Stelle könnte man das Verzeichnis <code>WT-NMP</code> zu einen Archive packen und hätte so schon mal eine gute Ausgangsbasis für eine fertige Sandbox. Man kann aber auch WT-NMP und WordPress komplett konfigurieren, so dass man in Zukunft nur noch das Zip-Archive entpacken muss um quasi WordPress Out-Of-The-Box starten zu können. Gehen wir zur Konfiguration über.</p>
<h3>Konfiguration der php.ini</h3>
<p>Eine Datei muss man auf alle Fälle anfassen, und zwar die <code>php.ini</code>. Hier gilt es einige Erweiterungen (Extensions) zu aktivieren und einige kleinere Einstellungen zu korrigieren. Wichtig ist jedoch das man <strong>nicht</strong> die <code>php.ini</code> aus dem PHP-Verzeichnis nimmt, sie dient als Vorlage wenn WT-NMP automatisch angepasst wird. Alle Dateien die wir anpassen müssen finden wir im Verzeichnis <code>conf</code>!</p>
<p>Beginnen wir mit den Extensions und öffnen die Datei <code>conf/php.ini</code> im Texteditor unserer Wahl. Die Extension <code>php_xcache</code> und die Zend Extension <code>php_opcache</code> <em>kann</em> man aktivieren, muss man aber nicht. Es gibt wohl einige Probleme mit diesen Erweiterungen, so dass man es mal ausprobieren muss ob man besser mit aktivierten oder deaktivierten Cache-Extensions zurecht kommt. Ein aktiviertes XDebug (die Erweiterung steht ganz am Ende der <code>php.ini</code>) gehört zum guten Ton und sollte aktiviert werden. Zudem musste ich auf meinem System (Windows7 64bit) noch <code>php_mysql</code> aktivieren da ich ansonsten keine Verbindung zur Datenbank bekommen habe.<br />
<code>memory_limit</code> sollte man von 64MB auf 128MB herauf setzen. Bei einer lokalen Installation können es auch gerne mehr sein damit PHP möglichst flüssig arbeitet. <code>short_open_tag</code> stellen wir mal brav auf <code>Off</code>, das sollte nirgendwo mehr aktiviert sein. Ob man <code>allow_url_fopen</code> zulässt oder nicht, muss man ggf. im Einzelfall entscheiden. Bei mir steht es eigentlich immer auf <code>On</code> da es vieles leichter macht.<br />
Eine Besonderheit ist noch unter <code>disable_classes</code> zu beachten. Hier sollte man alle deaktivierten PHP-Klassen entfernen da es sonst unter WordPress von SimplePie Fehlermeldungen hagelt. Auch die Liste darüber mit den <code>disable_functions</code> sollte man mal kurz durchschauen. In einer reinen Entwicklungsumgebung stehen dort einige Funktionen die man ganz gut gebrauchen kann und ansonsten nicht zur Verfügung stehen würden.<br />
Im Großen und Ganzen war es das auch schon mit den Änderungen an der <code>php.ini</code>. Erfahrene Benutzer können noch mal alle Einstellungen durchgehen und ihren Bedürfnissen anpassen, für die Mehrheit sollte es so passen.</p>
<h3>Eigener Server und Domäne</h3>
<p>Wenn man nicht ständig über <em>http://localhost/wordpress</em> auf sein Blog zugreifen möchte und stattdessen lieber eine eigene Domäne verwenden möchte, so muss man sich noch einen zusätzlichen Server in der <code>nginx.conf</code> anlegen und einen Eintrag in der <code>hosts</code> Datei von Windows.<br />
Der Server ist schnell angelegt. Dazu kopieren wir uns einfach den bereits vorhandenen Server und fügen die Kopie hinter den bestehenden an. Lediglich die Zeilen mit <code>listen</code>, <code>server_name</code> und <code>root</code> müssen wir anpassen. In der Zeile <code>listen</code> löschen wir nur den <code>default_server</code>, den <code>server_name</code> ändern wir auf unsere Wunsch-Domäne und bei <code>root</code> ergänzen wir <code>/wordpress</code> damit der <code>root</code>-Eintrag auch auf unsere WordPress-Installation zeigt.</p>
<pre class="brush:plain">	server {
		listen       127.0.0.1:80;
		server_name  samplewp.tld;

		root "d:/wt-nmp/www/wordpress";
		autoindex on;

		allow		127.0.0.1;
		deny		all;

		# deny access to .htaccess files, if Apache's document root
		# concurs with nginx's one
		#
		location ~ /\.ht {
		    deny  all;
		}

		location ~ \.php$ {
		    try_files $uri =404; 
	            fastcgi_pass	php_farm;
		    include		nginx.fastcgi.conf;
	        }

	}</pre>
<p>So sieht das ganze bei mir aus. Das ist lediglich eine Grundkonfiguration, wer mehr benötigt (z.B. URL-Rewriting), muss sich näher mit Nginx beschäftigen, dass geht etwas über diesen Artikel hinaus. In der Windows <code>hosts</code>-Datei machen wir dann noch den Eintrag für unsere Domäne was bei mir so aussieht: <code>127.0.0.1 samplewp.tld</code></p>
<p>An der <code>mysql.ini</code> musste ich nichts verändern, hier kann es aber sinnvoll sein die Einstellungen für <code>wait_timeout</code> und <code>connect_timeout</code> hoch zu setzen. Vor allem wenn man WT-NMP auf einen langsamen USB-Stick installiert können schon mal längere Wartezeiten auftreten.</p>
<h3>Erster Start</h3>
<div id="attachment_133" style="width: 310px" class="wp-caption alignright"><a class="highslide" onclick="return hs.expand(this)"  href="http://yoda.neun12.de/wp-content/uploads/2013/12/wtnmp_launcher.png"><img class="size-medium wp-image-133 " alt="WT-NMP Launcher" src="http://yoda.neun12.de/wp-content/uploads/2013/12/wtnmp_launcher-300x190.png" width="300" height="190" /></a><p class="wp-caption-text">Der WT-NMP Launcher</p></div>
<p>Nun müssen wir noch Nginx, MySQL und PHP starten. Das erfolgt recht bequem über den WT-NMP Launcher <em>WT-NMP.exe</em> im Verzeichnis <code>WT-NMP</code>. Mit dem Launcher lassen sich auch die einzelnen Prozesse von Nginx, MySQL und PHP starten und stoppen, was recht wichtig ist wenn man Änderungen an den jeweiligen Einstellungs-Dateien (ini-Dateien) vorgenommen hat. Im Launcher sieht man neben den einzelnen Komponenten auch noch ein paar Icons, was sich dahinter verbirgt wird angezeigt wenn man mit dem Mauspfeil über das jeweilige Icon verbleibt. Das einzig wirklich interessante dürfte der Datenbank-Manager (HeidiSQL) neben der MySQL-Komponente sein.<br />
Jetzt kann man auch schon loslegen und im Browser seiner Wahl testweise mal <em>http://localhost/wordpress</em> bzw. seine zuvor angelegte Domäne <em>http://samplewp.tld</em> aufrufen. WordPress wird uns nun mit der einen oder anderen Meldung beglücken das dies und jenes noch nicht vorhanden sei. Bisher haben wir ja auch weder Datenbank noch Datenbank-Benutzer oder eine <code>wp-config.php</code> angelegt.</p>
<h3>Datenbank anlegen</h3>
<div id="attachment_132" style="width: 310px" class="wp-caption alignleft"><a class="highslide" onclick="return hs.expand(this)"  href="http://yoda.neun12.de/wp-content/uploads/2013/12/wtnmp_home.png"><img class="size-medium wp-image-132 " alt="WT-NMP Startbildschirm" src="http://yoda.neun12.de/wp-content/uploads/2013/12/wtnmp_home-300x167.png" width="300" height="167" /></a><p class="wp-caption-text">Der Startbildschirm von WT-NMP im Browser</p></div>
<p>Beginnen wir mit dem Anlegen einer Datenbank. Dazu rufen wir im Browser einfach <em>http://localhost</em> auf und sollten die Startseite von WT-NMP sehen (siehe Screenshot). Mit einen Klick auf <em>Adminer Database Manager</em> gelangen wir zur Datenbankverwaltung. Es ist schon fast Geschmackssache ob man HeidiSQL oder Adminer verwendet. Adminer ist jedoch deutlich schlanker und übersichtlicher.<br />
Im Adminer oben rechts eine neue Datenbank anlegen, einen Namen für die Datenbank vergeben und den Rest erst mal so belassen wie es ist. Danach sollte man sich einen neuen Benutzer anlegen, dazu klickt man oben rechts auf <em>Rechte</em>. Nun werden die vorhandenen Benutzer angezeigt und unterhalb der Tabelle mit den Benutzern der Link für <em>Neuer Benutzer</em>. Erneut einen Namen eingeben, Passwort und die Checkboxen <em>All Privileges</em> und <em>Grand option</em> anhaken, mit <em>Speichern</em> bestätigen und das war es dann auch schon.</p>
<p>Auf in den Schlussspurt. Dazu öffnen wir die <code>wp-config-sample.php</code>-Datei aus unserer WordPress-Installation und passen sie wie gewohnt mit Datenbankname, Nutzer und Passwort an. Noch bequemer geht es eigentlich nur indem man <em>http://samplewp.tld</em> (bzw. <em>http://localhost/wordpress</em>) aufruft und die <code>wp-config.php</code> von WordPress erstellen lässt. Diejenigen unter uns die öfters WordPress einrichten und zusätzliche Angaben in der <code>wp-config.php</code> machen werden ersten Weg wählen, ansonsten reicht halt auch der bequeme Weg.</p>
<p>Damit sind wir eigentlich auch schon fertig. Ich denke mal wer öfters WordPress installiert und auch schon mal mit XAMPP gearbeitet hat, dürfte für alles zusammen kaum mehr als 10 Minuten benötigen. Wobei man sich die Arbeit ab jetzt auch sparen kann, denn es reicht aus das Verzeichnis <code>WT-NMP</code> als Zip-Archive abzuspeichern und hat ab dann immer ein frisches WordPress komplett installiert parat.</p>
<h3>Bonus-Runde</h3>
<div id="attachment_134" style="width: 310px" class="wp-caption alignleft"><a class="highslide" onclick="return hs.expand(this)"  href="http://yoda.neun12.de/wp-content/uploads/2013/12/adminer.png"><img class="size-medium wp-image-134" alt="Adminer" src="http://yoda.neun12.de/wp-content/uploads/2013/12/adminer-300x169.png" width="300" height="169" /></a><p class="wp-caption-text">Adminer im Einsatz</p></div>
<p>Also auf zur Bonus-Runde. Es gibt tatsächlich noch Server die mit PHP5.2 arbeiten bzw. Nutzer die PHP5.2 verwenden (müssen). Für einen Entwickler ist das recht lästig, muss man doch ständig irgendwo einen Server mit PHP5.2 parat haben für den Fall der Fälle das man was entwickeln oder testen will. WT-NMP macht uns hier das Leben sehr leicht. Es wird zwar mit PHP5.4 bzw. PHP5.5 ausgeliefert, jedoch ist es sehr einfach auch noch PHP5.2 nachzurüsten. Dazu benötigt man lediglich das Zip-Archive von PHP5.2 <strong>Non Thread Safe</strong>. Das NTS (Non Thread Safe) ist wichtig, da PHP ansonsten nicht starten will. Das Zip-Archive bekommt man bei <a title="windows.php.net - /downloads/releases/archives/" href="http://windows.php.net/downloads/releases/archives/" target="_blank">PHP.net im Museum</a> (letzte verfügbare Version dort ist <a title="PHP Museuum" href="http://windows.php.net/downloads/releases/archives/php-5.2.16-nts-Win32-VC6-x86.zip" target="_blank">PHP-5.2.16-nts</a> (Windows Binairies)). Wer mit der von mir verlinkten Version nichts anfangen kann, muss ein wenig nach der für sein Betriebssystem passenden Version suchen.<br />
Das Zip-Archive entpacken wir einfach in einen Ordner in <code>WT-NMP/bin/</code> (z.B. <code>WT-NMP/bin/php-5.2.16</code>). Der WT-NMP Launcher bietet uns ab nun die Wahl zwischen den installierten PHP-Versionen an. Zum Wechseln der PHP-Version einfach den PHP-Prozess stoppen, Version wechseln und der Neustart von PHP wird automatisch eingeleitet. Bequemer geht es nun doch wirklich nicht, oder!?<br />
Ich habe es nun nicht ausprobiert mit welchen PHP-Versionen das funktioniert. Da aber immer die gleiche <code>php.ini</code> verwendet wird, hängt es wohl in erster Linie davon ab ob die PHP-Version mit der vorhandenen <code>php.ini</code> zusammen arbeitet.</p>
<h3>Fazit</h3>
<p>Für mich ist WT-NMP momentan das ultimative Tool wenn es darum geht mit verschiedenen PHP-Versionen zu entwickeln oder WordPress in einer Sandbox oder auf einen USB-Stick zu installieren. Bisher habe ich nur ein wenig mit WT-NMP herum gespielt und mich noch nicht näher z.B. mit der Konfiguration von Nginx beschäftigt. Auch habe ich noch nicht getestet ob die Aktivierung der Cache-Extensions (XCache und OPcache) wirklich so negative Auswirkungen haben wie auf der WT-NMP Webseite beschrieben. Deswegen würde ich mich über ein paar Erfahrungsberichte, gerne auch in Form eines Blog-Posts, freuen.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-131/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wohin mit den Daten &#8211; Ab in den DataContainer</title>
		<link>http://yoda.neun12.de/artikel-121</link>
		<comments>http://yoda.neun12.de/artikel-121#comments</comments>
		<pubDate>Fri, 31 May 2013 13:07:36 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=121</guid>
		<description><![CDATA[Beim Schreiben von Plugins und Themes stößt man früher oder später auf ein Problem das WordPress recht pragmatisch gelöst hat: Der globale Zugriff auf Daten. Auf die Variable x die in Funktion y definiert wurde, kann nicht ohne weiteres in Funktion z zugegriffen werden. Das Problem ist der sogenannte Scope, also der Gültigkeitsbereich in PHP. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Beim Schreiben von Plugins und Themes stößt man früher oder später auf ein Problem das WordPress recht pragmatisch gelöst hat: Der globale Zugriff auf Daten.</p>
<p>Auf die Variable <code>x</code> die in Funktion <code>y</code> definiert wurde, kann nicht ohne weiteres in Funktion <code>z</code> zugegriffen werden. Das Problem ist der sogenannte Scope, also der Gültigkeitsbereich in PHP. WordPress umgeht das Problem einfach dadurch, indem es recht rücksichtslos fast alle seine Variablen mit den Schlüsselwort <code>global</code> in den globalen Namensraum verfrachtet und somit überall verfügbar macht. Das ist zwar eine sehr einfache Lösung, jedoch auch eine sehr egoistische. Nehmen wir einfach mal an wir hätten ein Plugin oder Theme das die Variable <code>foo</code> verwendet.</p>
<pre class="brush:php">&lt;?php
function set_foo( $var ) {
  global $foo;
  $foo = $var;
}

function print_something() {
  global $foo;
  print( "Foo is {$foo}&lt;br&gt;" );
}

function do_something() {
  global $foo;
  $foo = (int) $foo;
  $foo++;
}</pre>
<h3>Prefixen</h3>
<p>Nun kommt die nächste Version von WordPress raus und BÄMM, WordPress verwendet selber die Variable <code>foo</code> und macht sie mittels <code>global</code> auch noch überall verfügbar. Eine Katastrophe, denn nun müssten wir unser Plugin/Theme komplett umschreiben.<br />
Bisher vermied man dieses Problem indem man alle seine Variablen und Funktionen mit einem Prefix versah, also anstatt einfach nur <code>$foo</code> mit einem Prefix wie z.B. <code>$fb_foo</code>. Diese Lösung hat jedoch auch seine Nachteile. <a title="Blog | bueltge.de [by:ltge.de]" href="http://bueltge.de/home/">Frank Bültge</a> hatte seit jeher den Prefix <code>fb_</code> verwendet, dieser wird jedoch mittlerweile häufig im Zusammenhang mit FaceBook verwendet. Einen kurzen und recht eindeutigen Prefix zu finden, dürfte so langsam schwer werden. Könnte man hingehen und einen längeren Prefix verwenden, zum Beispiel <code>frabue_</code> der so lange funktioniert, bis jemand anderes ihn ebenfalls verwendet. Eine Rüstungsspirale die dazu führt das der Prefix irgendwann länger ist als der Variablen- oder Funktionsname.</p>
<h3>Namespaces</h3>
<p>Seit PHP5.3 haben wir die Möglichkeit <a title="PHP: Namespaces - Manual" href="http://php.net/manual/de/language.namespaces.php">Namespaces</a> zu nutzen. Namspaces lösen eine ganze Reihe solcher und vergleichbarer Probleme, weshalb ich inzwischen nur noch für PHP5.3+ schreibe. Man sollte aber auch nicht verschweigen, dass man sich angewöhnen muss seinen Code anders zu schreiben. Am Anfang bedeutet dies etwas Frust, der schwindet jedoch recht schnell weil die Vorteile überwiegen. Leider hat nicht jeder die Möglichkeit PHP5.2 einfach zu ignorieren. Arbeitet man im Kundenauftrag und weigert der Kunde sich beharrlich auf PHP5.3+ zu wechseln, kann man nicht auf Namespaces zurück greifen.</p>
<h3>Containern</h3>
<p>Aber wir können zumindest für unsere Variablen unseren eigenen kleinen Namespace basteln. Und den können wir dann auch noch wesentlich komfortabler ausstatten als den globalen Namensraum. Dazu nutzen wir eine Klasse die in der Grundversion aus lediglich zwei magischen Methoden besteht, <code>__set()</code> und <code>__get()</code>. Der Klasse geben wir noch eine statische Eigenschaft mit in der wir dann alle unsere Variablen speichern können. Der große &#8220;Zauber&#8221; an dieser Klasse besteht im Grunde genommen darin, dass statische Eigenschaften immer ihren Wert behalten, egal wie viele Instanzen wir von der Klasse erstellen.</p>
<pre class="brush:php">&lt;?php 
class DataContainer
{
  public static $data = array();

  public function __set( $name, $value ) {
    self::$data[$name] = $value;
  }

  public function __get( $name ) {
    if ( isset( self::$data[$name] ) )
      return self::$data[$name];
    else
      return null;
  }
}</pre>
<p>Die Verwendung der Klasse ist recht simpel. Wollen wir eine Variable setzen, erzeugen wir eine Instanz der Klasse und setzen die Variable. Wollen wir an einer anderen Stelle die gleiche Variable wieder zurück lesen, erzeugen wir eine Instanz und lesen ganz einfach die Variable.</p>
<pre class="brush:php">&lt;?php
function set_foo( $var ) {
  $dc = new DataContainer;
  $dc-&gt;foo = $var;
}

function print_something() {
  $dc = new DataContainer;
  $foo = $dc-&gt;foo;
  print( "Foo inside DataContainer: {$dc-&gt;foo}
 Foo as local variable: {$foo}
" );
}

function do_something() {
  $dc = new DataContainer;
  $foo = &amp;$dc::$data['foo'];
  $foo = (int) $foo;
  $foo++;
}

set_foo( '1' );
do_something();

$dc = new DataContainer();
echo "Foo is now: {$dc-&gt;foo}";</pre>
<p>Wie man an der Funktion <code>do_something()</code> sieht, ist es auch möglich die Variablen innerhalb vom DataContainer mittels Referenz zu bearbeiten, so dass man bearbeitete Werte nicht zwangsweise wieder zurück in den DataContainer schreiben muss.</p>
<h3>Vererbung</h3>
<p>Nun ist es unter Umständen etwas mühselig in jeden Funktionsaufruf eine neue Instanz des DataContainers zu erzeugen. Arbeitet man objektorientiert, ist es etwas einfacher, da die Klassen den DataContainer erweitern können.</p>
<pre class="brush:php">&lt;?php
class Bar extends DataContainer
{
  public function hello_foo() {
    printf( 'Foo used inside %s: %s
', __CLASS__, $this-&gt;foo );
  }
}

$bar = new Bar();
$bar-&gt;hello_foo();</pre>
<p>Alle Variablen die im DataContainer abgelegt, gelöscht oder geändert werden, sind in der Klasse <code>Bar</code> genauso verfügbar. Vererbung ist nicht gerade die Paradedisziplin von PHP weswegen man wohl eher auf <a title="What is Dependency Injection? - Fabien Potencier" href="http://fabien.potencier.org/article/11/what-is-dependency-injection">Dependency Injection</a> zurück greifen würde. Ein weiterer, meinem persönlichen Geschmack nach, besonders wertvoller Vorteil von DI ist, man kann seinen DataContainer Mocken und die testbarkeit seiner Klassen deutlich erhöhen. DI, Unittest und Mockings sind aber ein ganz anderes Thema.</p>
<h3>RODC &#8211; Read Only DataContainer</h3>
<p>Es kann natürlich sein das unser Theme oder Plugin durch Filter und Hooks erweitert werden kann, dann könnte es durchaus sein das wir einmal gesetzte Variablen davor schützen wollen das sie von unbekannten Code überschrieben werden. Nutzt man den globalen Namensraum, so ist das nahezu unmöglich. In WordPress gibt es z.B. keine Möglichkeit die globale Variable <code>$post</code>, die eine zentrale Rolle spielt, vor dem Überschreiben zu schützen. Jeder kann sie jederzeit löschen oder ändern, was dazu führt das andere Codeabschnitte, die diese Variable benötigen, unter Umständen versagen. Mit einem DataContainer ist das deutlich einfacher zu handhaben, wir bauen einfach eine Methode ein die bestimmte Variablen als <em>Read Only</em> kennzeichnet.</p>
<pre class="brush:php">&lt;?php
class DataContainer
{
  public static $data = array();

  public static $protected = array();

  public function __set( $name, $value ) {
    if ( ! in_array( $name, self::$protected ) )
      self::$data[$name] = $value;
  }

  public function __get( $name ) {
    if ( isset( self::$data[$name] ) )
      return self::$data[$name];
    else
      return null;
  }

  public static function protect( $name ) {
    if ( ! in_array( $name, self::$protected ) )
      array_push( self::$protected, $name );
  }
}

function good_foo() {
  $dc = new DataContainer();
  $dc-&gt;foo = 'protected foo';
  $dc-&gt;bar = 'unprotected bar';
  $dc-&gt;protect( 'foo' );
}

function bad_bar() {
  $dc = new DataContainer();
  $dc-&gt;foo = 'hahaha!';
  $dc-&gt;bar = 'I change everything!';
}

$dc = new DataContainer();

echo "Set &lt;code&gt;foo&lt;/code&gt; to &lt;b&gt;protected foo&lt;/b&gt; and &lt;code&gt;bar&lt;/code&gt; to &lt;b&gt;unprotected bar&lt;/b&gt;&lt;br&gt;";
good_foo();

echo "Try to overwrite &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt;&lt;br&gt;";
bad_bar();

echo "&lt;code&gt;foo&lt;/code&gt; is still &lt;b&gt;{$dc-&gt;foo}&lt;/b&gt; and &lt;code&gt;bar&lt;/code&gt; is now &lt;b&gt;{$dc-&gt;bar}&lt;/b&gt;&lt;br&gt;";</pre>
<p>Das dürfte nun nicht jeden Tag vorkommen das man seine Variablen schützen muss, zeigt aber schön welche Vorteile ein DataContainer gegenüber den globalen Namensraum hat. Man ihn nämlich nahezu nach belieben an seine Bedürfnisse anpassen.</p>
<h3>Shutdown</h3>
<p>Zum Schluss noch ein kleines Beispiel wie ich bei einem Plugin, an dem ich gerade schreibe, mit Hilfe eines DataContainers ein paar Probleme gelöst habe.<br />
Das Plugin erforderte die Zwischenspeicherung einiger Daten da diese zur Laufzeit erstellt werden und über verschiedene Seitenaufrufe verfügbar bleiben müssen. Gegeben sei also z.B. ein Array <code>$a</code> das zur Laufzeit befüllt wird und bei einem Ajax-Request verfügbar sein muss. Mein erster Lösungsansatz war die Verwendung von <code>$_SESSION</code>, denn genau dazu sind Sessions da. Um Daten über verschiedene Seitenaufrufe breit zu stellen. Um sicher zu gehen das am Ende des Skripts auch alle Daten in der Session gespeichert wurden, habe ich <code>shutdown</code> Hook von WordPress benutzt. Dieser Hook wird bei Beendigung des Skriptes ausgeführt, zu einem Zeitpunkt also, wenn alle Daten vorhanden sein sollten. In der Callback-Funktion habe ich dann einfach alle Daten in die Session geschrieben und die Session geschlossen.<br />
Nun taten sich verschiedene Probleme auf. Zum einen ist das recht unflexibel. Fügt man noch weitere Daten hinzu, muss man sich darum kümmern das auch diese in der Session gespeichert werden. Genauso wenn Daten entfallen, muss man sich darum kümmern das diese, nicht mehr vorhandenen Daten, nicht gespeichert werden. Der erste Lösungsansatz war dann anstatt einzelner Arrays einen DataContainer zu verwenden und dann den kompletten DataContainer in der Session zu speichern. Das funktioniert erstaunlich gut, jedoch musste ich mich immer noch selber darum kümmern das die Daten in der Session gespeichert werden.<br />
Zudem ist der <code>shutdown</code> Hook nicht wirklich sicher. Wird das Skript z.B. mit einem <code>exit()</code> <code>die()</code> beendet bevor der Hook registriert wird, werden die Daten nicht in der Session gespeichert. Dieses Problem lässt sich dadurch lösen, indem man das Speichern und lesen der Daten in bzw. aus der Session in den DataContainer verlagert.</p>
<pre class="brush:php">&lt;?php
class DataContainer
{
	const SESSION_KEY = 'Example_DataConatiner';

	public static $data = array();

	public function __construct() {
		( ! session_id() ) AND session_start();

		if ( isset( $_SESSION[self::SESSION_KEY] ) &amp;&amp; ! empty( $_SESSION[self::SESSION_KEY] ) )
			self::$data = $_SESSION[self::SESSION_KEY];

		register_shutdown_function( array( $this, '__destruct' ) );
	}

	public function __destruct() {
		( ! session_id() ) AND session_start();

		$_SESSION[self::SESSION_KEY] = self::$data;
		session_write_close();
	}

	public function __set( $name, $value ) {
		self::$data[$name] = $value;
	}

	public function __get( $name ) {
		if ( isset( self::$data[$name] ) )
			return self::$data[$name];
		else
			return null;
	}

	public static function reset() {
		self::$data = array();
		unset($_SESSION[self::SESSION_KEY]);
		session_write_close();
	}
}

$dc  = new DataContainer();
$uri = $_SERVER['PHP_SELF'];

if ( null === $dc-&gt;session_data ) {
	$dc-&gt;session_data = 'Value from previous page request';
	echo 'This is the first run&lt;br&gt;';
	die( "&lt;a href='{$uri}?run=2'&gt;Klick&lt;/a&gt;" );
}

$pagerequest = filter_input( INPUT_GET, 'run', FILTER_SANITIZE_NUMBER_INT );
echo "This is the {$pagerequest}nd run with &lt;b&gt;{$dc-&gt;session_data}&lt;/b&gt; from session-data";
$dc::reset();</pre>
<p>Das funktioniert schon recht wunderbar. Durch die <code>__destruct()</code> Methode wird der Inhalt des DataContainers in der Session gespeichert sobald er zerstört wird. Da PHP am Ende eines Skriptes alle bestehenden Objekte zerstört (schließt), wird die <code>__destruct</code> Methode automatisch am Ende eines Skriptes aufgerufen. Im Beispiel oben wird die <code>__destruct()</code> Methode mittels <code>register_shutdown_function()</code> noch zusätzlich registriert, so dass sie beim Beenden des Skriptes auch definitiv ausgeführt wird. Verwendet man anstatt der magischen <code>__destruct()</code> Methode eine eigene Methode und registriert sie mit <code>register_shutdown_function()</code>, so kann man den DataContainer verwerfen ohne das seine Werte gespeichert werden, die Werte werden jedoch automatisch am Ende des Skriptes gespeichert.</p>
<p>Allerdings wurden Sessions zu einen Zeitpunkt erfunden als Browser keine Tabs kannten und anscheinend geht jeder Browser etwas anders damit um. Ich hatte zumindest massive Probleme mit dem FireFox wenn ich mehr als einen Tab zum gleichen Server offen hatte (z.B. die Startseite in den einen Tab, einen Artikel in der Einzelansicht in einen anderen). Chrome und Opera können das besser, aber ich kann meinen Besuchern ja schlecht vorschreiben welchen Browser sie zu benutzen haben oder wie viele Tabs sie öffnen dürfen.<br />
WordPress bietet uns einen guten Ersatz für Sessions sofern man damit Probleme hat. Wir verwenden einfach <a title="Transients API « WordPress Codex" href="http://codex.wordpress.org/Transients_API">Transients</a> zum Speichern der Daten. Man muss hier abwägen ob die zusätzlichen DB-Zugriffe und vor allem die Menge an Daten die man speichern möchte dies rechtfertigt. Transients haben den Vorteil das man recht genau einstellen kann wie lange sie gültig sind. Das geht zwar mit <a title="PHP: session_set_cookie_params - Manual" href="http://php.net/manual/en/function.session-set-cookie-params.php">Sessions auch</a>, jedoch erlaubt nicht jeder Hoster das Verändern des Wertes für die Session-Lifetime. Transients sind sogar beständiger als Sessions, so kann man einen Transient an einen Login koppeln und somit eine Art von Server-Cookie umsetzen.</p>
<h3>Fazit</h3>
<p>Ich hoffe es ist deutlich geworden das der globale Namensraum eigentlich mehr Nach- als Vorteile hat und das man mit nur wenig Aufwand eine bessere Lösung bekommen kann die man nach seinen eigenen Bedürfnissen anpassen kann. Vor allem das automatische Speichern, ob in Sessions oder Transients, erleichtert die tägliche Arbeit enorm da man sich weniger Gedanken darum machen muss ob man nun alles gespeichert hat oder nicht. Der DataContainer lässt sich enorm ausbauen, denkbar wäre z.B. auch eine Methode die Werte aus einen Aufruf der Settings-API validiert und speichert. Am Ende des Skriptes werden die Daten automatisch in die Options geschrieben, stehen aber während der Laufzeit weiterhin zur Verfügung ohne das man sie sich extra aus den Options holen müsste.<br />
Vor allem das man den DataContainer, und somit auch Zugriffe auf Transients und die Options-Tabelle, sehr einfach mocken kann, erleichtert das Unittesting ungemein wodurch man wiederum viel Zeit und Mühe beim Programmieren spart.<br />
Alle in diesen Artikel verwendeten <a title="Example files for DataContainer" href="https://gist.github.com/RalfAlbert/5684779">Skripte sind als Gist</a> erhältlich für den Fall das sich jemand dazu ermuntert fühlt ein wenig mit DataContainern zu experimentieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-121/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Der kleine aber feine Unterschied zwischen is_email() und sanitize_email()</title>
		<link>http://yoda.neun12.de/artikel-104</link>
		<comments>http://yoda.neun12.de/artikel-104#comments</comments>
		<pubDate>Tue, 30 Apr 2013 11:53:15 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=104</guid>
		<description><![CDATA[Wer mit E-Mails in WordPress arbeitet, sollte den Unterschied zwischen is_email() und sanitize_email() kennen. Er ist zwar klein, aber fein. Der offensichtlichste Unterschied ist erst einmal der Rückgabewert. is_email() prüft ob eine gegebene E-Mail Adresse überhaupt den Kriterien entspricht und gibt einen String zurück wenn dem so ist. Andernfalls gibt is_email() false zurück. sanitize_email() hingegen [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Wer mit E-Mails in WordPress arbeitet, sollte den Unterschied zwischen <code>is_email()</code> und <code>sanitize_email()</code> kennen. Er ist zwar klein, aber fein.</p>
<p>Der offensichtlichste Unterschied ist erst einmal der Rückgabewert. <code>is_email()</code> prüft ob eine gegebene E-Mail Adresse überhaupt den Kriterien entspricht und gibt einen String zurück wenn dem so ist. Andernfalls gibt <code>is_email()</code> <code>false</code> zurück. <code>sanitize_email()</code> hingegen gibt entweder einen String mit einer gültigen E-Mail Adresse oder einen leeren String zurück.<br />
<code>is_email()</code> gibt also einen boolschen Wert oder einen String, <code>sanitize_email()</code> immer einen String zurück.</p>
<p>Nun könnte man mit einer einfachen Typumwandlung <code>is_email()</code> dazu bringen ebenfalls immer einen String zurück zu geben. <code>(string) is_email( $email )</code> würde sich nahezu gleich verhalten wie <code>sanitize_email()</code>. Im Erfolgsfall eine gültige E-Mail Adresse, im Fehlerfall einen leeren String. Stimmt das? Überprüfen wir es:</p>
<pre class="brush:php">$emails = array( 'foo@bar.com', 'baz-at-example-org', 'öttö@wördpäss.com', 'meh@cöm.com' );
echo '&lt;ol&gt;';
array_walk(
	$emails,
	function ($email) {
		$empty = 'an empty string';
		$is = (string) is_email( $email );
		$se = sanitize_email( $email );

		printf(
			'&lt;li&gt;%s - %s (%s)&lt;/li&gt;',
			( '' != $is ) ? $is : $empty,
			( '' != $se ) ? $se : $empty,
             $email
		);
	}
);
echo '&lt;/ol&gt;';</pre>
<p>Die Ausgabe sieht in etwa so aus:</p>
<blockquote>
<ol>
<li>foo@bar.com &#8211; foo@bar.com (foo@bar.com)</li>
<li>an empty string &#8211; an empty string (baz-at-example-org)</li>
<li>an empty string &#8211; tt@wrdpss.com (öttö@wördpäss.com)</li>
<li>an empty string &#8211; meh@cm.com (meh@cöm.com)</li>
</ol>
</blockquote>
<p>Wie man sehen kann, gibt <code>(string) is_email( $email )</code> wie erwartet bei ungültigen E-Mail Adressen einen leeren String zurück. Das WordPress glaubt das Umlaute in E-Mail Adressen nicht erlaubt seien, lass wir an dieser Stelle mal dahingestellt. WordPress kann halt (noch) nicht mit Umlautdomains umgehen.<br />
Viel interessanter ist die Ausgabe von <code>sanitize_email()</code>. Auch <code>sanitize_email()</code> kann nicht mit Umlauten umgehen, streicht sie aber einfach aus der E-Mail Adresse anstatt sie z.B. zu kodieren.</p>
<p>Das führt nun natürlich zu Problemen wenn man es nicht weiß. Sollte mal eine größere Anzahl an E-Mails nicht versendet werden, kann das mitunter daran liegen das man vergessen hat vor dem Speichern zu prüfen ob WordPress damit umgehen kann.</p>
<pre class="brush:php">// FALSCH
$user = array(
  'name' =&gt; 'Hans'
  'email' =&gt; sanitize_email( $email )
);
update_option( 'awesome_options', $user );

// Richtig
$user = array(
  'name' =&gt; 'Hans'
  'email' =&gt; sanitize_email( (is_email( $email ) )
);
update_option( 'awesome_options', $user );</pre>
<h3>Merke!</h3>
<p>Jede E-Mail Adresse sollte bevor sie mit <code>sanitize_email()</code> verarbeitet wird erst mit <code>is_email()</code> validiert werden. Ansonsten bekommt man E-Mail Adressen die vielleicht lustig aussehen, es aber definitiv nicht sind.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-104/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Metaboxen eine CSS-Klasse zuordnen</title>
		<link>http://yoda.neun12.de/artikel-97</link>
		<comments>http://yoda.neun12.de/artikel-97#comments</comments>
		<pubDate>Thu, 04 Apr 2013 15:22:33 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=97</guid>
		<description><![CDATA[Auf WPSE tauchte die Frage auf wie man eine Metabox im Backend per Voreinstellung minimiert darstellt. WordPress bietet hierfür keine Option an, obwohl es wahrscheinlich keine schlechte Idee wäre. Denn fügt man relativ viele Metaboxen ein, wird es schnell unübersichtlich. Um eine Metabox minimiert (geschlossen) darzustellen, benötigt sie die CSS-Klasse closed. Auch wenn man eine [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Auf <a title="WordPress StackExchange" href="http://wordpress.stackexchange.com/questions/94401/auto-close-hide-custom-metabox-set-default-state">WPSE tauchte die Frage auf</a> wie man eine Metabox im Backend per Voreinstellung minimiert darstellt. WordPress bietet hierfür keine Option an, obwohl es wahrscheinlich keine schlechte Idee wäre. Denn fügt man relativ viele Metaboxen ein, wird es schnell unübersichtlich. Um eine Metabox minimiert (geschlossen) darzustellen, benötigt sie die CSS-Klasse <code>closed</code>.<br />
Auch wenn man eine Metabox in Abhängigkeit eines bestimmten Wertes besonders hervorheben möchte, z.B. weil eine Aktion beim Speichern fehlgeschlagen ist, bietet WordPress von Haus aus keine Option für zusätzliche CSS-Klassen an. Wer seine Metaboxen anders gestalten möchte, würde also wahrscheinlich auf JavaScript zurück greifen.</p>
<p>Die einfachste Lösung ist wie so oft ein Filter, der allerdings etwas versteckt ist. Die Funktion <code>add_meta_box()</code> selber hat keinerlei Filter oder Actions in die man sich einhängen könnte.<br />
Dafür aber die Funktion die dafür zuständig ist die Titleleiste der Metabox darzustellen. Damit es etwas klarer ist um welchen Teil des HTMLs es hier geht, auszugsweise das HTML der Excerpt-Metabox:</p>
<pre class="brush:php">&lt;div id="postexcerpt" class="postbox"&gt;
  &lt;div class="handlediv" title="Zum umschalten klicken"&gt;
    &lt;h3 class="hndle"&gt;
      &lt;span&gt;Auszug&lt;/span&gt;
    &lt;/h3&gt;
   &lt;div class="inside"&gt;
[...]
    &lt;/div&gt; &lt;!-- Ende div .inside --&gt;
  &lt;/div&gt; &lt;!-- Ende div .handlediv --&gt;
&lt;/div&gt; &lt;!-- Ende div #postexcerpt --&gt;</pre>
<p>Der Filter kann CSS-Klassen zum äußersten Div-Container, hier im Beispiel mit der ID <code>postexcerpt</code>, hinzufügen. Somit ist es dann auch möglich im Stylesheet die nachfolgenden HTML-Elemente zu stylen.</p>
<pre class="brush:php">add_action( 'add_meta_boxes', 'add_my_metabox' );

function add_my_metabox() {
  $id       = 'my-metabox';
  $title    = 'My Metabox';
  $callback = 'my_metabox_content';
  $page     = 'post';

  add_meta_box( $id, $title, $callback, $page );

  add_filter( "postbox_classes_{$page}_{$id}", 'minify_my_metabox' );
}

function my_metabox_content() { ... }

/**
 * Add extra css-classes to a meta-box
 *
 * @param array $classes Array with css-classes
 */
function minify_my_metabox( $classes ) {
  array_push( $classes, 'closed' );

  return $classes;
}</pre>
<p>In der Funktion <code>add_my_metabox()</code> wird zunächst einmal ganz normal eine Metabox erzeugt. Anschließend wird jedoch noch ein Filter gesetzt der als Callback die Funktion <code>minify_my_metabox()</code> aufruft. Der Hook für den Filter muss man um die ID der Metabox und den Post-Type ergänzen. An dieser Stelle wird es ein wenig kompliziert. Im Codex wird der Begriff &#8220;<em>Post Type</em>&#8221; verwendet, da der Filter und die zugehörige Funktion jedoch undokumentiert sind, muss man im Quellcode nachschauen. Und dort wird &#8220;<em>page</em>&#8221; als Variablenname verwendet.<br />
Ich persönlich finde den Begriff &#8220;<em>page</em>&#8221; sinnvoller, da man mit der gleichen Methode auch z.B. die Widgets im Dashboard beeinflussen kann (siehe Beispiel unten). Als &#8220;<em>page</em>&#8221; müsste man dazu lediglich <code>dashboard</code> angeben und kann dann eigenen oder bereits vorhandenen Widgets, weitere CSS-Klassen hinzufügen. Im <a href="http://codex.wordpress.org/Function_Reference/remove_meta_box#Parameters">Codex</a> findet man bei <code>remove_meta_box()</code> noch ein paar weitere mögliche Werte für <em>page</em>. Hier wird übrigens wieder von <em>page</em> gesprochen, im Gegensatz zu <em>post type</em> bei <code>add_meta_box()</code>.</p>
<pre class="brush:php">function register_dash_widget(){

	$id   = 'debugoutput';
	$page = 'dashboard';

	wp_add_dashboard_widget(
		$id,
		'Debug Output',
		function () { echo 'Hello World!'; }
	);

	add_filter(
		"postbox_classes_{$page}_{$id}",
		function ( $classes ) {
			array_push( $classes, 'closed' );
			return $classes;
		}
	);

}

add_action( 'wp_dashboard_setup', 'register_dash_widget' );</pre>
<p>Die Callback-Funktion <code>minify_my_metabox()</code> ist dann wieder recht unspektakulär. Sie erwartet als einzigen Parameter ein Array mit den CSS-Klassen. Fügt man z.B. die Klasse <code>closed</code> hinzu und gibt das Array zurück, so wird die Metabox (oder das Widget) minimiert anstatt geöffnet dargestellt. Denkbar wäre es natürlich auch eine CSS-Klasse <code>alert</code> hinzuzufügen um eine Metabox (oder Widget) farblich hervorzuheben.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-97/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mit Git und dem WordPress-SVN arbeiten</title>
		<link>http://yoda.neun12.de/artikel-93</link>
		<comments>http://yoda.neun12.de/artikel-93#comments</comments>
		<pubDate>Sun, 03 Feb 2013 19:32:21 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Code-Snippets]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=93</guid>
		<description><![CDATA[In den vergangenen Tagen habe ich mein erstes Plugin im WordPress Repository veröffentlicht. Für jemanden der wie ich schon seit Version 1.2 mit WordPress arbeitet, eigentlich ein recht später Zeitpunkt. Die Gründe hierfür sind jedoch weitreichend und sehr verschieden. Neben den sehr restriktiven bürokratischen Hürden war vor allem der Umstand das es sich beim WordPress [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>In den vergangenen Tagen habe ich <a title="AvatarPlus" href="http://wordpress.org/extend/plugins/avatarplus/">mein erstes Plugin im WordPress Repository</a> veröffentlicht. Für jemanden der wie ich schon seit Version 1.2 mit WordPress arbeitet, eigentlich ein recht später Zeitpunkt. Die Gründe hierfür sind jedoch weitreichend und sehr verschieden. Neben den sehr restriktiven bürokratischen Hürden war vor allem der Umstand das es sich beim WordPress Repository um ein SVN-Repository handelt ein Grund dort keinen Code einzureichen. Warum WordPress unbedingt ein SVN-Repo betreiben muss, erschließt sich mir nach wie vor nicht. Es würde reichen wenn sie Zip-Archive der Plugins hosten, alles was ein SVN-Repo an Möglichkeiten bietet, könnte man aus dem Zip-Archiv ableiten. Aber egal, sei&#8217;s drum.<br />
Das allerdings nicht nur ich eine Abneigung gegen SVN habe, zeigte sich schnell in einer <a title="Google+ Beitrag zu AvatarPlus" href="https://plus.google.com/116844107236111670286/posts/9z4zwV3hNGX">kleinen Diskussion</a> die sich entwickelte als ich zu meinen Plugin auf Google+ einen Beitrag veröffentlichte. Kernpunkt der Diskussion war, wie man am besten Git und SVN unter einen Hut bekommt.<br />
Die Schwierigkeit dabei ist gar nicht einmal beide unter einen Hut zu bekommen, dafür gibt es <code>git svn</code>, sondern auch noch die zusätzlichen Hürden zu überwinden die von WordPress aufgestellt wurden. So darf man z.B. nicht jeden Commit den man durchführt ins WP-Repo überführen, sondern lediglich wenn man eine neue Version veröffentlicht. Zudem muss man die neue Version taggen. <code>git svn</code> kann weder das eine noch das andere wirklich sauber durchführen. Nutzt man <code>git svn</code>, muss man vor jeden Commit ins SVN-Repo die Commit-History bereinigen (squashen), taggen muss man dann sogar von Hand da auch hier <code>git svn</code> nicht sehr sauber arbeitet. Es mag sein das <code>git svn</code> dies sehrwohl kann und ich es einfach nicht heraus bekommen habe wie man das sauber hin bekommt, da lasse ich mich gerne eines besseren belehren, jedoch hatte ich irgendwann auch einfach keine Lust mehr mich mit <code>git svn</code> auseinander zusetzen.</p>
<h3>Ein paar Basics</h3>
<p>Im Grunde genommen nutze ich immer zwei Git-Repos. Zum einen ein lokales Repo im Ordner in dem ich den Code entwickele. Zum anderen ein entferntes (remote) Repo in dem ich meine Arbeit speichere. Viele nutzen als Remote-Repo GitHub, BitBucket oder einen Firmenserver. Ich nutze aus verschiedenen Gründen einen Ordner in meiner Dropbox. Da im späteren Verlauf eine lokale Kopie des SVN-Repos benötigt wird, empfiehlt es sich neben den üblichen Remote-Repos (GitHub, BitBucket, usw.) ein Remote-Repo auf der eigenen Festplatte einzurichten. Das muss nicht zwingend in einer Dropbox sein, bietet sich jedoch an wenn man, so wie ich zum Beispiel, an mehreren Rechnern oder im Team an einem Plugin arbeitet.<br />
Beispielhaft sieht die Ordnerstruktur nun so aus das <code>/d/local/plugin-name/</code> der Ordner ist in dem entwickelt wird und <code>/d/dropbox/plugin-name/</code> der Ordner für das Remote-Repo darstellt. Um es etwas einfacher zu machen verwende ich &#8216;<code>local</code>&#8216; als Namen für den lokalen Entwickelungs-Ordner und &#8216;<code>remote</code>&#8216; als Namen für den Ordner der das Remote-Repo enthält.</p>
<h3>Hooks</h3>
<p>Um das automatische Committen ins SVN-Repo durchzuführen hatte ich recht schnell <a title="Git Book (en)" href="http://git-scm.com/book/en/Customizing-Git-Git-Hooks"><code>git hooks</code></a> als Lösungsweg ausgespäht. Dank einer guten Dokumentation zu Git und sich selbst installierende Beispielen, fand ich dann auch schnell einen passenden Hook und wie ich ihn zu meinen Zwecken nutzen kann. Bei dem Hook handelt es sich um den <code>update hook</code>, der immer dann ausgeführt wird, wenn in ein Remote-Repo hinein gepusht wird. Um das zu verstehen, ein kurzer Einblick in die Hooks von Git.<br />
Es gibt Hooks die nur dann ausgeführt werden wenn eine Aktion im lokalen Repo durchgeführt wird (z.B. beim Committen). Und es gibt Hooks die nur im Remote-Repo ausgeführt werden (z.B. beim Pushen). Die Hooks unterscheiden sich allerdings nicht nur darin wann (und wo) sie ausgeführt werden, sondern auch darin, welche Parameter sie mitgegeben bekommen. Da es keinen Hook gibt den man im lokalen Repo nutzen kann sofern man feststellen möchte ob ein Tag gesetzt bzw. gepusht wurde, entschied ich mich auf das Remote-Repo auszuweichen wo es den <code>update hook</code> gibt der von Git die benötigten Parameter übergeben bekommt.</p>
<h3>Das Setup</h3>
<p>Nun müssen noch ein paar Kleinigkeiten eingerichtet werden. Das <code>git init</code> im <code>local</code> Ordner dürfte mittlerweile in Fleisch und Blut übergegangen sein. Als nächstes wird mit einer Zeile sowohl das <code>remote</code> Verzeichnis als auch die Kopie des SVN-Repos angelegt. Dazu nach <code>/d/dropbox/</code> wechseln, eine Shell öffnen und <code>svn checkout http://plugins.svn.wordpress.org/plugin-name</code> eingegeben. SVN erzeugt einen Ordner <code>plugin-name</code> der bereits alle für SVN benötigten Unterordner enthält. Da nun noch das Remote-Git-Repo fehlt, wird in den Ordner <code>plugin-name</code> gewechselt und mit <code>git init --bare</code> ein Remote-Git-Repo angelegt. Wer es mal ausprobiert, wird merken das <code>git svn</code> im Grunde genommen nichts anderes macht. Es werden einfach zwei Repos unterschiedlicher CVS in einen Ordner untergebracht. Zum Schluß wird der <code>remote</code> noch in <code>local</code> als Remote-Repo hinzugefügt: <code>git remote add dropbox /d/dropbox/plugin-name</code><br />
Jetzt ist das Setup fast geschafft, was noch fehlt ist das Script für den <code>update hook</code>. In <code>/d/dropbox/plugin-name/hooks/</code> finden sich einige Beispiele für <code>git hooks</code>. Die können alle gelöscht werden um es schön übersichtlich zu halten. Statt der Beispiel-Scripte wird das <a title="update script on GitHub" href="https://github.com/RalfAlbert/git2svn4wp/blob/master/update"><code>update</code> Script</a> hier hinein kopiert und damit ist das Setup abgeschlossen.</p>
<p>Jetzt können wir fleißig an unseren Code im Entwickelungs-Ordner <code>local</code> arbeiten. Als gute Entwickler committen wir oft und aussagekräftig. Am Ende eines jeden Tages (oder auch öfters), pushen wir unsere Arbeit in den <code>remote</code>. Wenn man nun einmal in den <code>remote</code> schaut, wird man merken das sich nichts (sichtbares) dort tut. Weder kommt Code hinzu noch wird ein SVN-Commit durchgeführt (lässt sich ganz einfach mit <code>svn log</code> prüfen). Dies ist jedoch auch genau so gewollt. WordPress verbietet es uns jeden Commit in das WP-SVN zu committen.<br />
Wie kommt nun unsere neue Version ins WP-SVN? Wenn es denn soweit ist das eine neue Version fertig ist, dann wird diese in Git getaggt: <code>git tag 1.0 -a -m 'A new version of plugin-name was released with version number 1.0'</code> oder mit einen schlichteren &#8220;un-annotated tag&#8221; <code>git tag 1.0</code>. Dieser Tag muss noch nach <code>remote</code> gepusht werden (<code>git push dropbox 1.0</code>), ab jetzt fängt die Magie des <code>update hooks</code> an zu wirken.<br />
Das Shell-Script macht nun folgendes:</p>
<ul>
<li>Prüfe ob ein Git-Repo im Ordner <code>trunk</code> vorhanden ist. Wenn nicht, dann erzeuge eines</li>
<li>Prüfe ob in diesen Git-Repo ein Remote-Repo namens <code>svn-master</code> vorhanden ist. Wenn nicht, dann füge es hinzu</li>
<li>Hole dir die Daten aus dem Remote-Repo <code>svn-master</code> in den <code>master</code> branch (<code>pull</code> = <code>fetch</code>&amp;<code>merge</code>)</li>
<li>Füge den neuen Code/Dateien im SVN-Repo hinzu und führe ein SVN-Commit durch</li>
<li>Erzeuge mit <code>svn copy</code> einen sauberen SVN-Tag im Remote-SVN-Repo</li>
</ul>
<p>Das <code>update</code> Script muss dabei auf einen kleinen Trick zurück greifen. Tags sind nichts anderes als Revisionen die einen bestimmten Zustand des Codes widerspiegeln. Normalerweise könnte man beim <code>pull</code> auch auf den Tag zugreifen, dieser existiert im Remote-Repo allerdings zu diesen Zeitpunkt noch nicht. Deswegen <strong>muss der Code erst in einen Commit zum Remote-Repo gepusht werden</strong>, da der Code anschließend aus den letzten Commit gezogen wird!<br />
Es gibt sicherlich Möglichkeiten auch Clientseitige Hooks, also Hooks die im lokalen Repo ausgeführt werden, zu nutzen. Dann müsste man jedoch bei jedem klonen des Remote-Repos diese Hooks neu einrichten. So läuft der Prozess mehr oder minder zentral ab und man muss alles nur einmal einrichten egal mit wie vielen Computern oder Teammitgliedern man das ganze nutzt.</p>
<h3>Fazit</h3>
<p>Das ganze ist noch etwas wackelig, vor allem was Merge-Konflikte angeht. Mir fehlen dazu bisher die praktischen Erfahrungen und weitere Tests. Da das Git-Repo im Verzeichnis <code>trunk</code> jedoch einzig und alleine dazu dient den Code in das <code>trunk</code> Verzeichnis zu kopieren, kann man hier sehr grob vorgehen und einfach den bestehenden Code gnadenlos überschreiben. Es finden sich dazu mehrere Methoden im Netz, ich bin mir nur noch nicht ganz sicher welche die beste ist. Wahrscheinlich ist es das Beste den Code aus dem Remote-Repo in einen neuen, temporären Branch zu kopieren, den SVN-Commit durchzuführen und den Branch anschließend wieder zu löschen. Wer es ganz hart mag, kann das Git-Repo auch einfach komplett löschen indem er den Ordner <code>.git</code> im Verzeichnis <code>trunk</code> löscht.<br />
Auf ein <code>svn update</code> verzichte ich bewusst, da dies ebenfalls zu Merge-Konflikten führen kann. Meine Lösung ist also nichts für diejenigen, die mit mehreren an einen WP-SVN-repo arbeiten. Hier sind andere Strategien gefragt die um etwas Handarbeit, vor allem beim Lösen von Merge-Konflikten, nicht drum herum kommen.</p>
<p>Diejenigen die sich mit Shell- bzw. Bash-Scripten auskennen, werden schnell erkennen das dies mein allererstes Bash-Script ist. Der Code ist definitiv nicht optimal, erfüllt jedoch seinen Zweck. Auch lässt sich das eine oder andere in Sachen SVN vielleicht noch optimieren, denn auch SVN war bisher nicht mein Fachgebiet (wird es wohl auch nie werden).<br />
Ein paar Kleinigkeiten sollten noch gelöst werden, so zum Beispiel das automatische Übernehmen der Commit-Nachricht bei annotated Tags. In meinen Augen aber eher Komfort-Funktionen die eine etwas geringere Priorität haben.<br />
Das es ein Repo ist, kann sich ja ohnehin jeder den Code ziehen und die Verbesserungen/Optimierungen einbauen die er meint das sie dort hin gehören. Da es sich um &#8220;Social Coding&#8221; handelt, würde es mich freuen wenn auch der eine oder andere Pull Request bei mir ankommt.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-93/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Eigene Felder bei der Benutzerregistrierung</title>
		<link>http://yoda.neun12.de/artikel-82</link>
		<comments>http://yoda.neun12.de/artikel-82#comments</comments>
		<pubDate>Sun, 02 Dec 2012 14:10:25 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=82</guid>
		<description><![CDATA[Es kann durchaus mal vorkommen das bei der Registrierung von Benutzern Felder benötigt werden die nicht vorhanden sind. Normalerweise benutzt man die Seite auf der man Benutzer bearbeitet (user edit screen) um zusätzliche Benutzerdaten abzulegen. Das ist aber manchmal etwas umständlich, da man erst den Benutzer anlegen und ihn in einen zweiten Schritt bearbeiten muss. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Es kann durchaus mal vorkommen das bei der Registrierung von Benutzern Felder benötigt werden die nicht vorhanden sind. Normalerweise benutzt man die Seite auf der man Benutzer bearbeitet (user edit screen) um zusätzliche Benutzerdaten abzulegen. Das ist aber manchmal etwas umständlich, da man erst den Benutzer anlegen und ihn in einen zweiten Schritt bearbeiten muss. Deutlich einfacher wäre es, könnte man Registrierung und zusätzliche Benutzerdaten in einen Durchgang erledigen. Diese Fragestellung tauchte auf <a title="WPSE" href="http://wordpress.stackexchange.com/questions/74332/show-biographical-info-while-creating-new-user/">WordPress StackExchange</a> auf und ich machte mir ein paar Gedanken darüber.</p>
<p>Mein erster Gedanke war, nein geht nicht. Denn das Registrierungsformular bietet weder Hooks noch Filter an die man nutzen könnte um eigene Felder einzufügen. Nun ist das Einfügen einer Tabellenzeile und eines Input-Feldes mit jQuery keine Raketenwissenschaft, beides ist mit ein paar Zeilen Code schnell erledigt.</p>
<div class="gistem"><div id="gist-4188469" class="gist">
    

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="o">&lt;?</span><span class="nx">php</span></div><div class='line' id='LC2'><span class="sd">/**</span></div><div class='line' id='LC3'><span class="sd"> * Plugin Name:	Custom user registration fields</span></div><div class='line' id='LC4'><span class="sd"> * Plugin URI:	http://yoda.neun12.de</span></div><div class='line' id='LC5'><span class="sd"> * Description:	Add custom fields to the user registration</span></div><div class='line' id='LC6'><span class="sd"> * Version: 	0.1</span></div><div class='line' id='LC7'><span class="sd"> * Author: 		Ralf Albert</span></div><div class='line' id='LC8'><span class="sd"> * Author URI: 	http://yoda.neun12.de</span></div><div class='line' id='LC9'><span class="sd"> * Text Domain:</span></div><div class='line' id='LC10'><span class="sd"> * Domain Path:</span></div><div class='line' id='LC11'><span class="sd"> * Network:</span></div><div class='line' id='LC12'><span class="sd"> * License:		GPLv3</span></div><div class='line' id='LC13'><span class="sd"> */</span></div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="nx">add_action</span><span class="p">(</span> <span class="s1">&#39;plugins_loaded&#39;</span><span class="p">,</span> <span class="s1">&#39;wp_custom_user_registration_fields&#39;</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span> <span class="p">);</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'><span class="k">function</span> <span class="nf">wp_custom_user_registration_fields</span><span class="p">(){</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'>	<span class="nx">add_action</span><span class="p">(</span></div><div class='line' id='LC21'>		<span class="s1">&#39;admin_print_scripts-user-new.php&#39;</span><span class="p">,</span></div><div class='line' id='LC22'>		<span class="k">function</span> <span class="p">(){</span></div><div class='line' id='LC23'><br/></div><div class='line' id='LC24'>			<span class="nx">wp_enqueue_script</span><span class="p">(</span></div><div class='line' id='LC25'>				<span class="s1">&#39;add_custom_user_registration_field&#39;</span><span class="p">,</span></div><div class='line' id='LC26'>				<span class="nx">plugins_url</span><span class="p">(</span> <span class="s1">&#39;wp_curf.js&#39;</span><span class="p">,</span> <span class="k">__FILE__</span> <span class="p">),</span></div><div class='line' id='LC27'>				<span class="k">array</span><span class="p">(</span> <span class="s1">&#39;jquery&#39;</span> <span class="p">),</span></div><div class='line' id='LC28'>				<span class="k">false</span><span class="p">,</span></div><div class='line' id='LC29'>				<span class="k">true</span></div><div class='line' id='LC30'>			<span class="p">);</span></div><div class='line' id='LC31'><br/></div><div class='line' id='LC32'>			<span class="nx">wp_enqueue_script</span><span class="p">(</span> <span class="s1">&#39;wp_curf_l10n&#39;</span> <span class="p">);</span></div><div class='line' id='LC33'><br/></div><div class='line' id='LC34'>			<span class="nx">wp_localize_script</span><span class="p">(</span></div><div class='line' id='LC35'>				<span class="s1">&#39;wp_curf_l10n&#39;</span><span class="p">,</span></div><div class='line' id='LC36'>				<span class="s1">&#39;wp_curf_l10n&#39;</span><span class="p">,</span></div><div class='line' id='LC37'>				<span class="k">array</span><span class="p">(</span></div><div class='line' id='LC38'>					<span class="s1">&#39;label&#39;</span>		<span class="o">=&gt;</span> <span class="nx">_</span><span class="p">(</span> <span class="s1">&#39;Biographical Info&#39;</span> <span class="p">),</span></div><div class='line' id='LC39'>					<span class="s1">&#39;description&#39;</span>	<span class="o">=&gt;</span> <span class="nx">_</span><span class="p">(</span> <span class="s1">&#39;Share a little biographical information to fill out your profile. This may be shown publicly.&#39;</span> <span class="p">)</span></div><div class='line' id='LC40'>				<span class="p">)</span></div><div class='line' id='LC41'>			<span class="p">);</span></div><div class='line' id='LC42'><br/></div><div class='line' id='LC43'>		<span class="p">}</span></div><div class='line' id='LC44'>	<span class="p">);</span></div><div class='line' id='LC45'><br/></div><div class='line' id='LC46'><span class="p">}</span></div><div class='line' id='LC47'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/4188469/3a97cfa4a77e049b6f07a08385faa07e865b61e5/index.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/4188469#file_index.php" style="float:right;margin-right:10px;color:#666">index.php</a>
            <a href="https://gist.github.com/4188469">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cm">/**</span></div><div class='line' id='LC2'><span class="cm"> * jQuery part of Costum User Registration Field</span></div><div class='line' id='LC3'><span class="cm"> *</span></div><div class='line' id='LC4'><span class="cm"> *  @author		Ralf Albert</span></div><div class='line' id='LC5'><span class="cm"> *  @version	0.1</span></div><div class='line' id='LC6'><span class="cm"> */</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="nx">jQuery</span><span class="p">(</span> <span class="nb">document</span> <span class="p">).</span><span class="nx">ready</span><span class="p">(</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">){</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">var</span> <span class="nx">insertElements</span> <span class="o">=</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;&lt;tr class=&quot;form-field&quot;&gt;&#39;</span> <span class="o">+</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;   &lt;th scope=&quot;row&quot;&gt;&lt;label for=&quot;description&quot;&gt;&#39;</span> <span class="o">+</span> <span class="nx">wp_curf_l10n</span><span class="p">.</span><span class="nx">label</span> <span class="o">+</span> <span class="s1">&#39;&lt;/label&gt;&lt;/th&gt;&#39;</span> <span class="o">+</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;   &lt;td&gt;&lt;textarea name=&quot;description&quot; id=&quot;description&quot; rows=&quot;5&quot; cols=&quot;30&quot;&gt;&lt;/textarea&gt;&lt;br /&gt;&lt;span class=&quot;description&quot;&gt;&#39;</span> <span class="o">+</span> <span class="nx">wp_curf_l10n</span><span class="p">.</span><span class="nx">description</span> <span class="o">+</span> <span class="s1">&#39;&lt;/span&gt;&lt;/td&gt;&#39;</span> <span class="o">+</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;&lt;/tr&gt;&#39;</span><span class="p">;</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">$</span><span class="p">(</span> <span class="s1">&#39;#createuser .form-table tbody&#39;</span> <span class="p">).</span><span class="nx">append</span><span class="p">(</span> <span class="nx">insertElements</span> <span class="p">);</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">}</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'><span class="p">);</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/4188469/70d1574a8bbabfd0170d0d139c16d8f95cdc7203/wp_curf.js" style="float:right;">view raw</a>
            <a href="https://gist.github.com/4188469#file_wp_curf.js" style="float:right;margin-right:10px;color:#666">wp_curf.js</a>
            <a href="https://gist.github.com/4188469">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>
</div>
</div><style type="text/css">@import "http://gist.github.com/stylesheets/gist/embed.css"; .gistem .highlight {background: inherit; !important;}</style>
<p>Das der Code so schlank und kompakt aussieht, liegt daran das ich die &#8220;Biographischen Angaben&#8221; aus dem Benutzer-Profil, also ein Standardfeld von WordPress, verwendet habe. WordPress verwendet sowohl für das Anlegen als auch für das Bearbeiten eines Benutzers nahezu die gleiche Routine. Somit muss ich mich nicht um die Speicherung des Feldinhaltes kümmern.<br />
Will ich nun ein komplett eigenes Feld erstellen, nennen wir es mal &#8220;Abteilung&#8221;, muss ich mich wieder um die Speicherung des Feldinhaltes und auch die Anzeige im Benutzerprofil, kümmern. Wie man eigene Felder dem Benutzerprofil hinzufügt und diese anzeigt, dazu gibt es im Netz zahlreiche Tutorials, zum Beispiel bei <a title="Add custom user fields" href="http://wpengineer.com/2173/custom-fields-wordpress-user-profile/">WP Engineer</a>.<br />
Da aber bereits bei der Registrierung eines Benutzers Daten gespeichert werden sollen, muss noch ein zusätzlichen Hook bemüht werden: <code>user_register</code>. Als Callback kann hier wieder die gleiche Funktion benutzt werden die auch beim Bearbeiten eines Benutzers verwendet wird, so sparen wir uns ein paar Zeilen Code.</p>
<p>Alles zusammen findet sich in diesen <a title="Gist@github" href="https://gist.github.com/4188847">Gist</a> und kann als Ausgangspunkt für ein eigenes Plugin verwendet werden.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-82/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Templating mit WordPress</title>
		<link>http://yoda.neun12.de/artikel-72</link>
		<comments>http://yoda.neun12.de/artikel-72#comments</comments>
		<pubDate>Sat, 26 May 2012 23:37:18 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=72</guid>
		<description><![CDATA[Mit diesen Artikel möchte ich zum Teil ein über 6 Monate altes Versprechen einlösen. Zum anderen ist es der dritte Teil einer Reihe von Beiträgen die Schritt für Schritt zu einer einfachen Template-Engine führen. Im ersten Teil hatte ich einen Formatter vorgestellt mit dem ich auf vergleichsweise einfache und flexible Art aus Daten eine Ausgabe [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Mit diesen Artikel möchte ich zum Teil ein <a title="Bültge - Trenne Ausgabe und Logik" href="http://bueltge.de/trenne-logik-von-der-ausgabe-mittels-hooks-in-wordpress/1335/#comment-440817">über 6 Monate altes Versprechen</a> einlösen. Zum anderen ist es der dritte Teil einer Reihe von Beiträgen die Schritt für Schritt zu einer einfachen Template-Engine führen.</p>
<p>Im <a title="Strings bequem formatieren" href="http://yoda.neun12.de/artikel-63">ersten Teil hatte ich einen Formatter vorgestellt</a> mit dem ich auf vergleichsweise einfache und flexible Art aus Daten eine Ausgabe machen kann. Im <a title="Trenne Ausgabe und Logik" href="http://yoda.neun12.de/artikel-65">zweiten Teil</a> habe ich mittels der Formatter-Klasse und einer Templates-Klasse eine Mini-Template-Engine gebastelt. Die hat aber noch etliche Nachteile und diente lediglich der Darstellung des Prinzips das dahinter steckt.<br />
Diesmal möchte ich auf eine konkrete Implementierung in WordPress eingehen die in dieser Form immer wieder verwendet werden kann.<span id="more-72"></span></p>
<h3>Das Pferd von hinten aufgezäumt</h3>
<p>Es ist vielleicht etwas ungewöhnlich, ich will jedoch von hinten anfangen und beginne mit den eigentlichen Templates. Das ist in sofern sinnvoll, als das wir konkrete Templates haben und uns dann einen Weg überlegen können wie wir an diese heran kommen. Hier also erst einmal die Template-Klassen:<br />
<div class="gistem"><div id="gist-2794775" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;?php</span></div><div class='line' id='LC2'><span class="sd">/**</span></div><div class='line' id='LC3'><span class="sd"> * </span></div><div class='line' id='LC4'><span class="sd"> * Abstract class Wp Simple Templates define one method to retrive the defined templates</span></div><div class='line' id='LC5'><span class="sd"> * @author Ralf Albert</span></div><div class='line' id='LC6'><span class="sd"> *</span></div><div class='line' id='LC7'><span class="sd"> */</span></div><div class='line' id='LC8'><span class="k">abstract</span> <span class="k">class</span> <span class="nc">WP_Simple_Templates</span></div><div class='line' id='LC9'><span class="p">{</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>	<span class="sd">/**</span></div><div class='line' id='LC12'><span class="sd">	 * </span></div><div class='line' id='LC13'><span class="sd">	 * Returns the template if it was defined in the template class. If the requested template was not</span></div><div class='line' id='LC14'><span class="sd">	 * defined in the template-class, return a wp-error.</span></div><div class='line' id='LC15'><span class="sd">	 * @param string $type</span></div><div class='line' id='LC16'><span class="sd">	 * @return array|string|object Array with template-strings or a single template-string or wp-error on failure</span></div><div class='line' id='LC17'><span class="sd">	 */</span></div><div class='line' id='LC18'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">(</span> <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="p">){</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'>		<span class="k">if</span><span class="p">(</span> <span class="s1">&#39;&#39;</span> <span class="o">==</span> <span class="nv">$type</span> <span class="p">)</span></div><div class='line' id='LC21'>			<span class="k">return</span> <span class="k">NULL</span><span class="p">;</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'>		<span class="c1">// if the template-class have a method $type, return the template(s). else return a wp-error</span></div><div class='line' id='LC24'>		<span class="k">if</span><span class="p">(</span> <span class="nb">method_exists</span><span class="p">(</span> <span class="nv">$this</span><span class="p">,</span> <span class="nv">$type</span> <span class="o">.</span> <span class="s1">&#39;_template&#39;</span> <span class="p">)</span> <span class="p">){</span></div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>			<span class="nv">$method</span> <span class="o">=</span> <span class="nv">$type</span> <span class="o">.</span> <span class="s1">&#39;_template&#39;</span><span class="p">;</span></div><div class='line' id='LC27'>			<span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nv">$method</span><span class="p">();</span></div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'>		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>			<span class="k">return</span> <span class="k">new</span> <span class="nx">WP_error</span><span class="p">(</span></div><div class='line' id='LC32'>				<span class="s1">&#39;template_error&#39;</span><span class="p">,</span></div><div class='line' id='LC33'>				<span class="nb">printf</span><span class="p">(</span></div><div class='line' id='LC34'>					<span class="s1">&#39;&lt;h4&gt;Template Error&lt;/h4&gt;Template &lt;strong&gt;%s&lt;/strong&gt; does not exists in &lt;strong&gt;%s&lt;/strong&gt;&#39;</span><span class="p">,</span></div><div class='line' id='LC35'>					<span class="nv">$type</span><span class="p">,</span></div><div class='line' id='LC36'>					<span class="nb">get_class</span><span class="p">(</span> <span class="nv">$this</span> <span class="p">)</span></div><div class='line' id='LC37'>				<span class="p">)</span></div><div class='line' id='LC38'>			<span class="p">);</span></div><div class='line' id='LC39'><br/></div><div class='line' id='LC40'>		<span class="p">}</span></div><div class='line' id='LC41'><br/></div><div class='line' id='LC42'>	<span class="p">}</span></div><div class='line' id='LC43'><br/></div><div class='line' id='LC44'>	<span class="sd">/**</span></div><div class='line' id='LC45'><span class="sd">	 * </span></div><div class='line' id='LC46'><span class="sd">	 * Return an array with all available templates from the template-class</span></div><div class='line' id='LC47'><span class="sd">	 */</span></div><div class='line' id='LC48'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_available_templates</span><span class="p">(){</span></div><div class='line' id='LC49'><br/></div><div class='line' id='LC50'>		<span class="c1">// get all methods from the abstract class (__CLASS__) and the extended class ($this)</span></div><div class='line' id='LC51'>		<span class="nv">$self_methods</span>		<span class="o">=</span> <span class="nb">get_class_methods</span><span class="p">(</span> <span class="nx">__CLASS__</span> <span class="p">);</span></div><div class='line' id='LC52'>		<span class="nv">$extended_methods</span>	<span class="o">=</span> <span class="nb">get_class_methods</span><span class="p">(</span> <span class="nv">$this</span> <span class="p">);</span></div><div class='line' id='LC53'><br/></div><div class='line' id='LC54'>		<span class="c1">// remove the &#39;_template&#39; extension and return the array with template-names</span></div><div class='line' id='LC55'>		<span class="nv">$templates</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span></div><div class='line' id='LC56'><br/></div><div class='line' id='LC57'>		<span class="k">foreach</span><span class="p">(</span> <span class="nb">array_diff</span><span class="p">(</span> <span class="nv">$extended_methods</span><span class="p">,</span> <span class="nv">$self_methods</span> <span class="p">)</span> <span class="k">as</span> <span class="nv">$template</span> <span class="p">)</span></div><div class='line' id='LC58'>			<span class="nb">array_push</span><span class="p">(</span> <span class="nv">$templates</span><span class="p">,</span> <span class="nb">str_replace</span><span class="p">(</span> <span class="s1">&#39;_template&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$template</span> <span class="p">)</span> <span class="p">);</span></div><div class='line' id='LC59'><br/></div><div class='line' id='LC60'>		<span class="k">return</span> <span class="nv">$templates</span><span class="p">;</span></div><div class='line' id='LC61'><br/></div><div class='line' id='LC62'>	<span class="p">}</span></div><div class='line' id='LC63'><br/></div><div class='line' id='LC64'><span class="p">}</span></div><div class='line' id='LC65'><br/></div><div class='line' id='LC66'><span class="sd">/**</span></div><div class='line' id='LC67'><span class="sd"> * </span></div><div class='line' id='LC68'><span class="sd"> * Concrete class WP Simple HTML Templates defines the templates</span></div><div class='line' id='LC69'><span class="sd"> * @author Ralf Albert</span></div><div class='line' id='LC70'><span class="sd"> *</span></div><div class='line' id='LC71'><span class="sd"> */</span></div><div class='line' id='LC72'><br/></div><div class='line' id='LC73'><span class="k">class</span> <span class="nc">WP_Simple_HTML_Templates</span> <span class="k">extends</span> <span class="nx">WP_Simple_Templates</span></div><div class='line' id='LC74'><span class="p">{</span></div><div class='line' id='LC75'>	<span class="sd">/**</span></div><div class='line' id='LC76'><span class="sd">	 * Returns an array with list templates</span></div><div class='line' id='LC77'><span class="sd">	 */</span></div><div class='line' id='LC78'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">list_template</span><span class="p">(){</span></div><div class='line' id='LC79'><br/></div><div class='line' id='LC80'>		<span class="k">return</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC81'>			<span class="s1">&#39;ol&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC82'>						<span class="s1">&#39;outer&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;ol&gt;</span><span class="se">n</span><span class="s2">%inner%&lt;/ol&gt;</span><span class="se">n</span><span class="s2">&quot;</span><span class="p">,</span></div><div class='line' id='LC83'>						<span class="s1">&#39;inner&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;li&gt;%item%&lt;/li&gt;</span><span class="se">n</span><span class="s2">&quot;</span></div><div class='line' id='LC84'>			<span class="p">),</span></div><div class='line' id='LC85'><br/></div><div class='line' id='LC86'>			<span class="s1">&#39;ul&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC87'>						<span class="s1">&#39;outer&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;ul&gt;</span><span class="se">n</span><span class="s2">%inner%&lt;/ul&gt;</span><span class="se">n</span><span class="s2">&quot;</span><span class="p">,</span></div><div class='line' id='LC88'>						<span class="s1">&#39;inner&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;li&gt;%item%&lt;/li&gt;</span><span class="se">n</span><span class="s2">&quot;</span></div><div class='line' id='LC89'>			<span class="p">),</span></div><div class='line' id='LC90'><br/></div><div class='line' id='LC91'>			<span class="s1">&#39;div&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC92'>						<span class="s1">&#39;outer&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;div&gt;</span><span class="se">n</span><span class="s2">%inner%&lt;/div&gt;</span><span class="se">n</span><span class="s2">&quot;</span><span class="p">,</span></div><div class='line' id='LC93'>						<span class="s1">&#39;inner&#39;</span>	<span class="o">=&gt;</span> <span class="s2">&quot;&lt;p&gt;%item%&lt;/p&gt;</span><span class="se">n</span><span class="s2">&quot;</span></div><div class='line' id='LC94'>			<span class="p">),</span></div><div class='line' id='LC95'>		<span class="p">);</span></div><div class='line' id='LC96'><br/></div><div class='line' id='LC97'>	<span class="p">}</span></div><div class='line' id='LC98'><br/></div><div class='line' id='LC99'>	<span class="sd">/**</span></div><div class='line' id='LC100'><span class="sd">	 * Returns a headline template with h1-tags</span></div><div class='line' id='LC101'><span class="sd">	 */</span></div><div class='line' id='LC102'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">hone_template</span><span class="p">(){</span></div><div class='line' id='LC103'><br/></div><div class='line' id='LC104'>		<span class="k">return</span> <span class="s1">&#39;&lt;h1&gt;%headline%&lt;/h1&gt;&#39;</span><span class="p">;</span></div><div class='line' id='LC105'><br/></div><div class='line' id='LC106'>	<span class="p">}</span></div><div class='line' id='LC107'><br/></div><div class='line' id='LC108'>	<span class="sd">/**</span></div><div class='line' id='LC109'><span class="sd">	 * Returns a paragraph-template with p-tags</span></div><div class='line' id='LC110'><span class="sd">	 */</span></div><div class='line' id='LC111'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">paragraph_template</span><span class="p">(){</span></div><div class='line' id='LC112'><br/></div><div class='line' id='LC113'>		<span class="k">return</span> <span class="s1">&#39;&lt;p&gt;%text%&lt;/p&gt;&#39;</span><span class="p">;</span></div><div class='line' id='LC114'><br/></div><div class='line' id='LC115'>	<span class="p">}</span></div><div class='line' id='LC116'><br/></div><div class='line' id='LC117'><span class="p">}</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2794775/e52df76e6d825281e1cd79d4bbc4adb4a25d9469/file1.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2794775#file_file1.php" style="float:right;margin-right:10px;color:#666">file1.php</a>
            <a href="https://gist.github.com/2794775">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>
</div>
</div></p>
<p>Am Anfang sehen wir eine abstrakte Klasse <code>WP_Simple_Templates</code>. Diese Klasse hat lediglich zwei Methoden (<code>get_templates()</code> und <code>get_available_templates()</code>) und da sie als abstrakt definiert wurde, kann man von ihr keine Instanz (kein Objekt) erzeugen. Die zweite Klasse enthält lediglich Methoden die nichts anderes machen als Strings und Arrays zurück zu geben.<br />
Wir könnten uns jetzt die abstrakte Klasse sparen und direkt auf die Methoden der Template-Klasse zugreifen. Dazu muss man aber immer genau wissen welche Methoden in der Template-Klasse definiert wurden. Das ist irgendwie doof, besser wäre es doch wenn man die Klasse fragen kann und sie antwortet mit einer Liste der verfügbaren Templates. Genau diese Aufgabe übernimmt die Methode <code>get_available_templates()</code> in der abstrakten Klasse.<br />
Um an die einzelnen Templates heran zu kommen gibt es die Methode <code>get_templates()</code>. Sie liefert immer ein Array mit den angeforderten Template(s) zurück. Es muss uns also gar nicht mehr interessieren wie die Templates in der Template-Klasse benannt wurden oder wie die Template-Klasse diese erzeugt, mit <code>get_template( 'list' )</code> bekommen wir immer das Template für HTML-Listen geliefert.<br />
Da wir später noch verschiedene Template-Klassen verwenden wollen, habe ich die Logik der Template-Klasse (<code>get_template()</code> und <code>get_available_templates()</code>) in eine abstrakte Klasse ohne Template-Strings ausgelagert und erweitere die Template-Klassen um diese abstrakte Klasse. Einfaches Code-Recyling halt.</p>
<h3>Die Templates verarbeiten</h3>
<p>Nun nützt es uns recht wenig wenn wir an die Templates heran kommen. Im Grunde genommen wollen wir das auch gar nicht. Das einzige was wir machen wollen ist, Daten angeben und eine Ausgabe zurück bekommen. Wir benötigen also noch eine Klasse die unsere Daten in die Templates einfügt:<div class="gistem"><div id="gist-2795515" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;?php</span></div><div class='line' id='LC2'><span class="sd">/**</span></div><div class='line' id='LC3'><span class="sd"> * </span></div><div class='line' id='LC4'><span class="sd"> * Abstract class WP Simple Templater creates a copy of the template-class and</span></div><div class='line' id='LC5'><span class="sd"> * provide the templates to the template-engine</span></div><div class='line' id='LC6'><span class="sd"> * @author Ralf Albert</span></div><div class='line' id='LC7'><span class="sd"> *</span></div><div class='line' id='LC8'><span class="sd"> */</span></div><div class='line' id='LC9'><span class="k">abstract</span> <span class="k">class</span> <span class="nc">WP_Simple_Templater</span> <span class="k">extends</span> <span class="nx">Formatter</span></div><div class='line' id='LC10'><span class="p">{</span></div><div class='line' id='LC11'>	<span class="sd">/**</span></div><div class='line' id='LC12'><span class="sd">	 * </span></div><div class='line' id='LC13'><span class="sd">	 * Instance of template-class</span></div><div class='line' id='LC14'><span class="sd">	 * @var object $templates_object</span></div><div class='line' id='LC15'><span class="sd">	 */</span></div><div class='line' id='LC16'>	<span class="k">protected</span> <span class="nv">$templates_object</span> <span class="o">=</span> <span class="k">NULL</span><span class="p">;</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>	<span class="sd">/**</span></div><div class='line' id='LC19'><span class="sd">	 * </span></div><div class='line' id='LC20'><span class="sd">	 * Constructor</span></div><div class='line' id='LC21'><span class="sd">	 * Creates an instance of the template-class and setup the delimiters for Formatter</span></div><div class='line' id='LC22'><span class="sd">	 * @param WP_Simple_Templates $templates</span></div><div class='line' id='LC23'><span class="sd">	 */</span></div><div class='line' id='LC24'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span> <span class="nx">WP_Simple_Templates</span> <span class="nv">$templates</span> <span class="p">){</span></div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates_object</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nv">$templates</span><span class="p">;</span></div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">set_delimiter</span><span class="p">(</span> <span class="s1">&#39;%&#39;</span><span class="p">,</span> <span class="s1">&#39;%&#39;</span> <span class="p">);</span></div><div class='line' id='LC29'><br/></div><div class='line' id='LC30'>	<span class="p">}</span></div><div class='line' id='LC31'><br/></div><div class='line' id='LC32'>	<span class="sd">/**</span></div><div class='line' id='LC33'><span class="sd">	 * </span></div><div class='line' id='LC34'><span class="sd">	 * Return a template by given type. Returns an wp-error on failure</span></div><div class='line' id='LC35'><span class="sd">	 * @param string $type</span></div><div class='line' id='LC36'><span class="sd">	 * @return string|bool Return a template-string if it was defined by the template-class. Or false if no such template was defined</span></div><div class='line' id='LC37'><span class="sd">	 */</span></div><div class='line' id='LC38'>	<span class="k">protected</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">(</span> <span class="nv">$templatetype</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="p">){</span></div><div class='line' id='LC39'><br/></div><div class='line' id='LC40'>		<span class="c1">// error message</span></div><div class='line' id='LC41'>		<span class="nv">$err_msg</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span></div><div class='line' id='LC42'><br/></div><div class='line' id='LC43'>		<span class="c1">// if the sub-type is empty (e.g the template is a string not an array), use the template-type as sub-type</span></div><div class='line' id='LC44'>		<span class="k">if</span><span class="p">(</span> <span class="s1">&#39;&#39;</span> <span class="o">==</span> <span class="nv">$type</span> <span class="p">)</span></div><div class='line' id='LC45'>			<span class="nv">$type</span> <span class="o">=</span> <span class="nv">$templatetype</span><span class="p">;</span></div><div class='line' id='LC46'><br/></div><div class='line' id='LC47'>		<span class="c1">// first check if a template-type was set</span></div><div class='line' id='LC48'>		<span class="k">if</span><span class="p">(</span> <span class="s1">&#39;&#39;</span> <span class="o">==</span> <span class="nv">$templatetype</span> <span class="p">)</span></div><div class='line' id='LC49'>			<span class="nv">$err_msg</span> <span class="o">=</span> <span class="s1">&#39;Empty template-type&#39;</span><span class="p">;</span></div><div class='line' id='LC50'><br/></div><div class='line' id='LC51'>		<span class="c1">// check if a template-class was loaded</span></div><div class='line' id='LC52'>		<span class="k">elseif</span><span class="p">(</span> <span class="k">NULL</span> <span class="o">===</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates_object</span> <span class="p">)</span></div><div class='line' id='LC53'>			<span class="nv">$err_msg</span> <span class="o">=</span> <span class="s1">&#39;No templates defined in &lt;strong&gt;&#39;</span> <span class="o">.</span> <span class="nb">get_class</span><span class="p">(</span> <span class="nv">$this</span> <span class="p">)</span> <span class="o">.</span> <span class="s1">&#39;&lt;/strong&gt;&#39;</span><span class="p">;</span>					</div><div class='line' id='LC54'><br/></div><div class='line' id='LC55'>		<span class="c1">// we have a template-type and a template-class. get the template(s)</span></div><div class='line' id='LC56'>		<span class="k">else</span> <span class="p">{</span></div><div class='line' id='LC57'><br/></div><div class='line' id='LC58'>			 <span class="nv">$template</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates_object</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">(</span> <span class="nv">$templatetype</span> <span class="p">);</span></div><div class='line' id='LC59'><br/></div><div class='line' id='LC60'>			 <span class="c1">// get error-message thrown by template-class</span></div><div class='line' id='LC61'>			 <span class="k">if</span><span class="p">(</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$template</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC62'>			 	<span class="nv">$err_msg</span> <span class="o">=</span> <span class="nv">$template</span><span class="o">-&gt;</span><span class="na">get_error_message</span><span class="p">();</span></div><div class='line' id='LC63'><br/></div><div class='line' id='LC64'>			 <span class="c1">// check the template.</span></div><div class='line' id='LC65'>			 <span class="c1">// convert a string into an array. else check if the requested type is in the template-array</span></div><div class='line' id='LC66'>			 <span class="k">else</span> <span class="p">{</span></div><div class='line' id='LC67'><br/></div><div class='line' id='LC68'>				 <span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nb">is_array</span><span class="p">(</span> <span class="nv">$template</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC69'>				 	<span class="nv">$template</span><span class="p">[</span><span class="nv">$type</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$template</span><span class="p">;</span></div><div class='line' id='LC70'><br/></div><div class='line' id='LC71'>				 <span class="k">elseif</span><span class="p">(</span> <span class="o">!</span> <span class="nb">in_array</span><span class="p">(</span> <span class="nv">$type</span><span class="p">,</span> <span class="nb">array_keys</span><span class="p">(</span> <span class="nv">$template</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC72'>				 	<span class="nv">$err_msg</span> <span class="o">=</span> <span class="nv">$type</span> <span class="o">.</span> <span class="s1">&#39; is not defined.&#39;</span><span class="p">;</span></div><div class='line' id='LC73'><br/></div><div class='line' id='LC74'>			 <span class="p">}</span></div><div class='line' id='LC75'><br/></div><div class='line' id='LC76'>		<span class="p">}</span></div><div class='line' id='LC77'><br/></div><div class='line' id='LC78'>		<span class="c1">// if a error occurs, return an error-object</span></div><div class='line' id='LC79'>		<span class="k">if</span><span class="p">(</span> <span class="s1">&#39;&#39;</span> <span class="o">!=</span> <span class="nv">$err_msg</span> <span class="p">){</span></div><div class='line' id='LC80'><br/></div><div class='line' id='LC81'>			<span class="k">return</span> <span class="k">new</span> <span class="nx">WP_Error</span><span class="p">(</span></div><div class='line' id='LC82'>					<span class="s1">&#39;template_error&#39;</span><span class="p">,</span></div><div class='line' id='LC83'>					<span class="nb">sprintf</span><span class="p">(</span></div><div class='line' id='LC84'>						<span class="s1">&#39;&lt;div class=&quot;error&quot;&gt;&lt;h4&gt;Template Error&lt;/h4&gt;%s&lt;/div&gt;&#39;</span><span class="p">,</span></div><div class='line' id='LC85'>						<span class="nv">$err_msg</span></div><div class='line' id='LC86'>					<span class="p">)</span></div><div class='line' id='LC87'>				<span class="p">);</span></div><div class='line' id='LC88'><br/></div><div class='line' id='LC89'>		<span class="p">}</span></div><div class='line' id='LC90'><br/></div><div class='line' id='LC91'>		<span class="c1">// finally all checks are ok, return the template-array</span></div><div class='line' id='LC92'>		<span class="k">return</span> <span class="nv">$template</span><span class="p">;</span></div><div class='line' id='LC93'><br/></div><div class='line' id='LC94'>	<span class="p">}</span></div><div class='line' id='LC95'><br/></div><div class='line' id='LC96'><span class="p">}</span></div><div class='line' id='LC97'><br/></div><div class='line' id='LC98'><span class="sd">/**</span></div><div class='line' id='LC99'><span class="sd"> * </span></div><div class='line' id='LC100'><span class="sd"> * The concrete class WP Simple HTML insert the data in the templates</span></div><div class='line' id='LC101'><span class="sd"> * @author Ralf Albert</span></div><div class='line' id='LC102'><span class="sd"> *</span></div><div class='line' id='LC103'><span class="sd"> */</span></div><div class='line' id='LC104'><span class="k">class</span> <span class="nc">WP_Simple_HTML</span> <span class="k">extends</span> <span class="nx">WP_Simple_Templater</span></div><div class='line' id='LC105'><span class="p">{</span></div><div class='line' id='LC106'><br/></div><div class='line' id='LC107'>	<span class="sd">/**</span></div><div class='line' id='LC108'><span class="sd">	 * </span></div><div class='line' id='LC109'><span class="sd">	 * Constructor overrides the delimiters in parent class</span></div><div class='line' id='LC110'><span class="sd">	 * @param WP_Simple_Templates $templates</span></div><div class='line' id='LC111'><span class="sd">	 */</span></div><div class='line' id='LC112'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span> <span class="nx">WP_Simple_Templates</span> <span class="nv">$templates</span> <span class="p">){</span></div><div class='line' id='LC113'><br/></div><div class='line' id='LC114'>		<span class="k">parent</span><span class="o">::</span><span class="na">__construct</span><span class="p">(</span> <span class="nv">$templates</span> <span class="p">);</span></div><div class='line' id='LC115'><br/></div><div class='line' id='LC116'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">set_delimiter</span><span class="p">(</span> <span class="s1">&#39;{&#39;</span><span class="p">,</span> <span class="s1">&#39;}&#39;</span> <span class="p">);</span></div><div class='line' id='LC117'><br/></div><div class='line' id='LC118'>	<span class="p">}</span></div><div class='line' id='LC119'><br/></div><div class='line' id='LC120'>	<span class="sd">/**</span></div><div class='line' id='LC121'><span class="sd">	 * </span></div><div class='line' id='LC122'><span class="sd">	 * Create a html list (ul, ol or with div-tag)</span></div><div class='line' id='LC123'><span class="sd">	 * @param string $type The type of the list. ul, ol or div</span></div><div class='line' id='LC124'><span class="sd">	 * @param array $data Data to display inside the list</span></div><div class='line' id='LC125'><span class="sd">	 */</span></div><div class='line' id='LC126'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_list</span><span class="p">(</span> <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="o">=</span> <span class="k">array</span><span class="p">()</span> <span class="p">){</span></div><div class='line' id='LC127'><br/></div><div class='line' id='LC128'>		<span class="c1">// get list templates</span></div><div class='line' id='LC129'>		<span class="nv">$templates</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">(</span> <span class="s1">&#39;list&#39;</span><span class="p">,</span> <span class="nv">$type</span> <span class="p">);</span></div><div class='line' id='LC130'><br/></div><div class='line' id='LC131'>		<span class="k">if</span><span class="p">(</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$templates</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC132'>			<span class="k">return</span> <span class="nv">$templates</span><span class="o">-&gt;</span><span class="na">get_error_message</span><span class="p">();</span></div><div class='line' id='LC133'><br/></div><div class='line' id='LC134'>		<span class="c1">// create list</span></div><div class='line' id='LC135'>		<span class="nv">$inner</span>	<span class="o">=</span> <span class="k">new</span> <span class="k">stdClass</span><span class="p">();</span></div><div class='line' id='LC136'>		<span class="nv">$values</span>	<span class="o">=</span> <span class="k">new</span> <span class="k">stdClass</span><span class="p">();</span></div><div class='line' id='LC137'><br/></div><div class='line' id='LC138'>		<span class="k">foreach</span><span class="p">(</span> <span class="nv">$data</span> <span class="k">as</span> <span class="nv">$key</span> <span class="o">=&gt;</span> <span class="nv">$value</span> <span class="p">){</span></div><div class='line' id='LC139'><br/></div><div class='line' id='LC140'>			<span class="nv">$values</span><span class="o">-&gt;</span><span class="na">key</span>	<span class="o">=</span> <span class="nv">$key</span><span class="p">;</span></div><div class='line' id='LC141'>			<span class="nv">$values</span><span class="o">-&gt;</span><span class="na">item</span>	<span class="o">=</span> <span class="nv">$value</span><span class="p">;</span></div><div class='line' id='LC142'><br/></div><div class='line' id='LC143'>			<span class="nv">$inner</span><span class="o">-&gt;</span><span class="na">inner</span>	<span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">sprintf</span><span class="p">(</span> <span class="nv">$templates</span><span class="p">[</span><span class="nv">$type</span><span class="p">][</span><span class="s1">&#39;inner&#39;</span><span class="p">],</span> <span class="nv">$values</span> <span class="p">);</span></div><div class='line' id='LC144'><br/></div><div class='line' id='LC145'>		<span class="p">}</span></div><div class='line' id='LC146'><br/></div><div class='line' id='LC147'>		<span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="na">sprintf</span><span class="p">(</span> <span class="nv">$templates</span><span class="p">[</span><span class="nv">$type</span><span class="p">][</span><span class="s1">&#39;outer&#39;</span><span class="p">],</span> <span class="nv">$inner</span> <span class="p">);</span></div><div class='line' id='LC148'><br/></div><div class='line' id='LC149'>	<span class="p">}</span></div><div class='line' id='LC150'><br/></div><div class='line' id='LC151'>	<span class="sd">/**</span></div><div class='line' id='LC152'><span class="sd">	 * </span></div><div class='line' id='LC153'><span class="sd">	 * Prints a html-list (ul, ol or with div-tag)</span></div><div class='line' id='LC154'><span class="sd">	 * @param string $type The type of the list. ul, ol or div</span></div><div class='line' id='LC155'><span class="sd">	 * @param array $data Data to display inside the list</span></div><div class='line' id='LC156'><span class="sd">	 */</span></div><div class='line' id='LC157'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">print_list</span><span class="p">(</span> <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="o">=</span> <span class="k">array</span><span class="p">()</span> <span class="p">){</span></div><div class='line' id='LC158'><br/></div><div class='line' id='LC159'>		<span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nb">is_array</span><span class="p">(</span> <span class="nv">$data</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC160'>			<span class="nv">$data</span> <span class="o">=</span> <span class="p">(</span><span class="k">array</span><span class="p">)</span> <span class="nv">$data</span><span class="p">;</span></div><div class='line' id='LC161'><br/></div><div class='line' id='LC162'>		<span class="k">echo</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="nv">$type</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC163'><br/></div><div class='line' id='LC164'>	<span class="p">}</span></div><div class='line' id='LC165'><br/></div><div class='line' id='LC166'>	<span class="sd">/**</span></div><div class='line' id='LC167'><span class="sd">	 * </span></div><div class='line' id='LC168'><span class="sd">	 * Create a H1-headline</span></div><div class='line' id='LC169'><span class="sd">	 * @param string $text</span></div><div class='line' id='LC170'><span class="sd">	 */</span></div><div class='line' id='LC171'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_headline</span><span class="p">(</span> <span class="nv">$text</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="p">){</span></div><div class='line' id='LC172'><br/></div><div class='line' id='LC173'>		<span class="c1">// get headline template</span></div><div class='line' id='LC174'>		<span class="nv">$template</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">(</span> <span class="s1">&#39;hone&#39;</span> <span class="p">);</span></div><div class='line' id='LC175'><br/></div><div class='line' id='LC176'>		<span class="k">if</span><span class="p">(</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$template</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC177'>			<span class="k">return</span> <span class="nv">$template</span><span class="o">-&gt;</span><span class="na">get_error_message</span><span class="p">();</span></div><div class='line' id='LC178'><br/></div><div class='line' id='LC179'>		<span class="nv">$data</span> <span class="o">=</span> <span class="k">new</span> <span class="k">stdClass</span><span class="p">();</span></div><div class='line' id='LC180'>		<span class="nv">$data</span><span class="o">-&gt;</span><span class="na">headline</span> <span class="o">=</span> <span class="nv">$text</span><span class="p">;</span></div><div class='line' id='LC181'><br/></div><div class='line' id='LC182'>		<span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="na">sprintf</span><span class="p">(</span> <span class="nv">$template</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC183'><br/></div><div class='line' id='LC184'>	<span class="p">}</span></div><div class='line' id='LC185'><br/></div><div class='line' id='LC186'>	<span class="sd">/**</span></div><div class='line' id='LC187'><span class="sd">	 * </span></div><div class='line' id='LC188'><span class="sd">	 * Print a H1-headline</span></div><div class='line' id='LC189'><span class="sd">	 * @param string $text</span></div><div class='line' id='LC190'><span class="sd">	 */</span></div><div class='line' id='LC191'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">print_headline</span><span class="p">(</span> <span class="nv">$text</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span> <span class="p">){</span></div><div class='line' id='LC192'><br/></div><div class='line' id='LC193'>		<span class="k">echo</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_headline</span><span class="p">(</span> <span class="nv">$text</span> <span class="p">);</span></div><div class='line' id='LC194'><br/></div><div class='line' id='LC195'>	<span class="p">}</span></div><div class='line' id='LC196'><br/></div><div class='line' id='LC197'><span class="p">}</span></div><div class='line' id='LC198'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2795515/0738580053b964b267b3bc830921eb1782ef2445/file1.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2795515#file_file1.php" style="float:right;margin-right:10px;color:#666">file1.php</a>
            <a href="https://gist.github.com/2795515">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>
</div>
</div><br />
Dies wird die Klasse <code>WP_Simple_HTML</code> übernehmen. Auch hier könnte man es sich einfach machen und sich den Template-String direkt aus der Template-Klasse holen und dann mit den entsprechenden Daten versehen an die Formatter-Klasse übergeben. Allerdings müssten wir dann zuerst die Daten jedes mal für das entsprechende Template aufbereiten. Diese Logik lagern wir lieber in die Klasse <code>WP_Simple_HTML</code> aus um sie nicht ständig im Plugin- oder Theme-Code drin zu haben.<br />
Das Heranholen der Templates aus der Template-Klasse ist immer der gleiche Vorgang, deswegen können wir diesen ebenfalls in eine abstrakte Klasse (<code>WP_Simple_Templater</code>) auslagern die wir ebenfalls immer wieder verwenden können.<br />
Diese Klasse hat eine kleine Besonderheit. Im Konstruktor wird explizit ein Objekt angefordert das von der Klasse <code>WP_Simple_Templates</code> abstammt. Da wir <code>WP_Simple_Templates</code> abstrakt definiert haben, und somit davon nicht direkt eine Instanz erzeugen können, muss es also eine Instanz einer Klasse sein die <code>WP_Simple_Templates</code> erweitert. Damit gehen wir sicher das wir <code>WP_Simple_HTML</code> nicht z.B. eine Instanz von <code>wpdb</code> (der Datenbank-Klasse) oder ein Error-Object (<code>WP_Error()</code>) übergeben. Die abstrakte Klasse erfüllt somit neben dem Code-Recycling zusätzlich noch die Aufgabe sicherzustellen das wir eine Template-Klasse und nichts anderes übergeben.</p>
<p>Damit haben wir jetzt die Logik von der Ausgabe getrennt. Sowohl in unserer Template-Klasse (<code>WP_Simple_Templates</code> und <code>WP_Simple_HTML_Templates</code>), als auch in der Template-Engine (<code>WP_Simple_Templater</code> und <code>WP_Simple_HTML</code>). Wiederkehrende Logik haben wir in abstrakte Klassen ausgelagert die wir problemlos recyclen können.<br />
Die einzige Arbeit die uns zukünftig bleibt, ist eine Template-Klasse zu schreiben die die entsprechenden Templates enthält und für jedes Template die nötige Logik schreiben die die Daten aufbereitet und anschließend Daten und Template verbindet. Da wir die Logik für die Datenaufbereitung von den Templates getrennt haben, können wir munter die Templates umstellen ohne in unseren eigentlichen Plugin- oder Theme-Code eingreifen zu müssen.<br />
Die Logik für die Datenaufbereitung müssen wir auch nur dann anfassen, wenn wir die Templates um weitere Variablen ergänzen oder ändern. Logik und Ausgabe sind in allen Bereichen komplett voneinander getrennt.</p>
<h3>Lose koppeln anstatt fest verdrahten</h3>
<p>Wie würde man nun diese Template-Engine in seinem Plugin oder Theme verwenden? Wahrscheinlich würde man sie an entsprechender Stelle so im Code einfügen:</p>
<pre class="brush:php">$template = new WP_Simple_HTML_Templates();
$html = new WP_Simple_HTML( $template );
//some other code

$html-&gt;print_headline( 'Success!' );

$list = $html-&gt;get_list( 'ol', array( 'one' =&gt; 'eins', 'two' =&gt; 'zwei', 'three' =&gt; 'drei' ) );

// more code
echo $list;</pre>
<p>Das sieht im Grunde genommen schon mal schön aus. Relativ schön, um genau zu sein. Denn sowohl die Template-Klasse (<code>$template</code>) als auch die Template-Engine (<code>$html</code>) sind mehr oder minder fest verdrahtet. Würden wir für unsere Template-Engine eine andere Template-Klasse verwenden wollen, müssten wir dies im Code ändern. Gleiches gilt für den Fall das wir unsere Template-Klasse behalten aber auf eine andere Template-Engine zurückgreifen wollen.</p>
<p>Besser wäre es, wenn wir später noch andere Template-Engines und Template-Klassen angeben könnten. WordPress bietet uns hierzu die Möglichkeit mit <code>apply_filters()</code> und <code>add_filter()</code>. Schauen wir uns dazu folgende Demo an:<div class="gistem"><div id="gist-2795518" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;?php</span></div><div class='line' id='LC2'><span class="k">class</span> <span class="nc">Template_Engine_Test</span></div><div class='line' id='LC3'><span class="p">{</span></div><div class='line' id='LC4'>	<span class="sd">/**</span></div><div class='line' id='LC5'><span class="sd">	 * </span></div><div class='line' id='LC6'><span class="sd">	 * Container for the different template-engines</span></div><div class='line' id='LC7'><span class="sd">	 * @var object $template_engine</span></div><div class='line' id='LC8'><span class="sd">	 */</span></div><div class='line' id='LC9'>	<span class="k">public</span> <span class="k">static</span> <span class="nv">$template_engine</span><span class="p">;</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>	<span class="sd">/**</span></div><div class='line' id='LC12'><span class="sd">	 * </span></div><div class='line' id='LC13'><span class="sd">	 * Initial some vars</span></div><div class='line' id='LC14'><span class="sd">	 */</span></div><div class='line' id='LC15'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(){</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>		<span class="c1">// initialize the template-engine to avoid strict errors</span></div><div class='line' id='LC18'>		<span class="nx">self</span><span class="o">::</span><span class="nv">$template_engine</span> <span class="o">=</span> <span class="k">new</span> <span class="k">stdClass</span><span class="p">();</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'>	<span class="p">}</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>	<span class="sd">/**</span></div><div class='line' id='LC23'><span class="sd">	 * </span></div><div class='line' id='LC24'><span class="sd">	 * Caching for the template-classes</span></div><div class='line' id='LC25'><span class="sd">	 * Create a WP-Error if the template-engine could not be initialize</span></div><div class='line' id='LC26'><span class="sd">	 * @param string $template_engine The requested template-engine</span></div><div class='line' id='LC27'><span class="sd">	 * @return object The requested template-engine if available</span></div><div class='line' id='LC28'><span class="sd">	 */</span></div><div class='line' id='LC29'>	<span class="k">protected</span> <span class="k">function</span> <span class="nf">get_template_engine</span><span class="p">(</span> <span class="nv">$template_engine</span> <span class="p">){</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>		<span class="nv">$template_engine</span> <span class="o">=</span> <span class="nx">strtolower</span><span class="p">(</span> <span class="nv">$template_engine</span> <span class="p">);</span></div><div class='line' id='LC32'><br/></div><div class='line' id='LC33'>		<span class="c1">// available template-engines and the template-classes</span></div><div class='line' id='LC34'>		<span class="nv">$available_template_engines</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC35'><br/></div><div class='line' id='LC36'>			<span class="s1">&#39;html&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span> <span class="s1">&#39;WP_Simple_HTML&#39;</span><span class="p">,</span> <span class="s1">&#39;WP_Simple_HTML_Templates&#39;</span> <span class="p">)</span></div><div class='line' id='LC37'><br/></div><div class='line' id='LC38'>		<span class="p">);</span></div><div class='line' id='LC39'><br/></div><div class='line' id='LC40'>		<span class="nv">$available_template_engines</span> <span class="o">=</span> <span class="nx">apply_filters</span><span class="p">(</span> <span class="s1">&#39;available_template_engines&#39;</span><span class="p">,</span> <span class="nv">$available_template_engines</span> <span class="p">);</span></div><div class='line' id='LC41'><br/></div><div class='line' id='LC42'>		<span class="c1">// check if the requested template-engine is available. if not, try to create it</span></div><div class='line' id='LC43'>		<span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nb">isset</span><span class="p">(</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$template_engine</span><span class="o">-&gt;</span><span class="nv">$template_engine</span> <span class="p">)</span> <span class="p">){</span></div><div class='line' id='LC44'><br/></div><div class='line' id='LC45'>			<span class="k">if</span><span class="p">(</span> <span class="o">!</span> <span class="nb">isset</span><span class="p">(</span> <span class="nv">$available_template_engines</span><span class="p">[</span><span class="nv">$template_engine</span><span class="p">]</span> <span class="p">)</span> <span class="p">){</span></div><div class='line' id='LC46'><br/></div><div class='line' id='LC47'>				<span class="k">return</span> <span class="k">new</span> <span class="nx">WP_Error</span><span class="p">(</span> <span class="s1">&#39;template_engine&#39;</span><span class="p">,</span> <span class="s1">&#39;Unknown template-engine &lt;strong&gt;&#39;</span> <span class="o">.</span> <span class="nv">$template_engine</span> <span class="o">.</span> <span class="s1">&#39;&lt;/strong&gt;&#39;</span> <span class="p">);</span></div><div class='line' id='LC48'><br/></div><div class='line' id='LC49'>			<span class="p">}</span> <span class="k">else</span> <span class="p">{</span></div><div class='line' id='LC50'><br/></div><div class='line' id='LC51'>				<span class="nv">$tmpl_ng</span>	<span class="o">=</span> <span class="o">&amp;</span><span class="nv">$available_template_engines</span><span class="p">[</span><span class="nv">$template_engine</span><span class="p">];</span></div><div class='line' id='LC52'>				<span class="nv">$class</span>		<span class="o">=</span> <span class="o">&amp;</span><span class="nv">$tmpl_ng</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span></div><div class='line' id='LC53'>				<span class="nv">$template</span>	<span class="o">=</span> <span class="o">&amp;</span><span class="nv">$tmpl_ng</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span></div><div class='line' id='LC54'><br/></div><div class='line' id='LC55'>				<span class="nx">self</span><span class="o">::</span><span class="nv">$template_engine</span><span class="o">-&gt;</span><span class="nv">$template_engine</span> <span class="o">=</span> <span class="k">new</span> <span class="nv">$class</span><span class="p">(</span> <span class="k">new</span> <span class="nv">$template</span> <span class="p">);</span></div><div class='line' id='LC56'><br/></div><div class='line' id='LC57'>			<span class="p">}</span></div><div class='line' id='LC58'><br/></div><div class='line' id='LC59'>		<span class="p">}</span></div><div class='line' id='LC60'><br/></div><div class='line' id='LC61'>		<span class="k">return</span> <span class="nx">self</span><span class="o">::</span><span class="nv">$template_engine</span><span class="o">-&gt;</span><span class="nv">$template_engine</span><span class="p">;</span></div><div class='line' id='LC62'><br/></div><div class='line' id='LC63'>	<span class="p">}</span> </div><div class='line' id='LC64'><br/></div><div class='line' id='LC65'>	<span class="sd">/**</span></div><div class='line' id='LC66'><span class="sd">	 * </span></div><div class='line' id='LC67'><span class="sd">	 * The output</span></div><div class='line' id='LC68'><span class="sd">	 */</span></div><div class='line' id='LC69'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">output</span><span class="p">(</span> <span class="nv">$stream</span> <span class="p">){</span></div><div class='line' id='LC70'><br/></div><div class='line' id='LC71'>		<span class="nv">$output</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_template_engine</span><span class="p">(</span> <span class="nv">$stream</span> <span class="p">);</span></div><div class='line' id='LC72'><br/></div><div class='line' id='LC73'>		<span class="k">if</span><span class="p">(</span> <span class="nx">is_wp_error</span><span class="p">(</span> <span class="nv">$output</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC74'>			<span class="k">die</span><span class="p">(</span> <span class="nv">$output</span><span class="o">-&gt;</span><span class="na">get_error_message</span><span class="p">()</span> <span class="p">);</span></div><div class='line' id='LC75'><br/></div><div class='line' id='LC76'>		<span class="nv">$output</span><span class="o">-&gt;</span><span class="na">print_headline</span><span class="p">(</span> <span class="nx">strtoupper</span><span class="p">(</span> <span class="nv">$stream</span> <span class="p">)</span> <span class="p">);</span></div><div class='line' id='LC77'><br/></div><div class='line' id='LC78'>		<span class="nv">$list</span> <span class="o">=</span> <span class="nv">$output</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="s1">&#39;ol&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="s1">&#39;eins&#39;</span><span class="p">,</span> <span class="s1">&#39;zwei&#39;</span><span class="p">,</span> <span class="s1">&#39;drei&#39;</span> <span class="p">)</span> <span class="p">);</span></div><div class='line' id='LC79'>		<span class="k">echo</span> <span class="nv">$list</span><span class="p">;</span></div><div class='line' id='LC80'><br/></div><div class='line' id='LC81'>	<span class="p">}</span></div><div class='line' id='LC82'><br/></div><div class='line' id='LC83'>	<span class="sd">/**</span></div><div class='line' id='LC84'><span class="sd">	 * </span></div><div class='line' id='LC85'><span class="sd">	 * Setup html-mode</span></div><div class='line' id='LC86'><span class="sd">	 */</span></div><div class='line' id='LC87'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">set_html</span><span class="p">(){</span></div><div class='line' id='LC88'><br/></div><div class='line' id='LC89'>		<span class="c1">// html is build in</span></div><div class='line' id='LC90'><br/></div><div class='line' id='LC91'>	<span class="p">}</span></div><div class='line' id='LC92'><br/></div><div class='line' id='LC93'>	<span class="sd">/**</span></div><div class='line' id='LC94'><span class="sd">	 * </span></div><div class='line' id='LC95'><span class="sd">	 * Setup xml-mode</span></div><div class='line' id='LC96'><span class="sd">	 */</span></div><div class='line' id='LC97'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">set_xml</span><span class="p">(){</span></div><div class='line' id='LC98'><br/></div><div class='line' id='LC99'>		<span class="nx">add_filter</span><span class="p">(</span> <span class="s1">&#39;available_template_engines&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="o">&amp;</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;add_xml&#39;</span> <span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span> <span class="p">);</span></div><div class='line' id='LC100'><br/></div><div class='line' id='LC101'>	<span class="p">}</span></div><div class='line' id='LC102'><br/></div><div class='line' id='LC103'>	<span class="sd">/**</span></div><div class='line' id='LC104'><span class="sd">	 * </span></div><div class='line' id='LC105'><span class="sd">	 * Add the XML-Template-Engine</span></div><div class='line' id='LC106'><span class="sd">	 */</span></div><div class='line' id='LC107'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">add_xml</span><span class="p">(){</span></div><div class='line' id='LC108'><br/></div><div class='line' id='LC109'>		<span class="k">return</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC110'><br/></div><div class='line' id='LC111'>			<span class="s1">&#39;xml&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span> <span class="s1">&#39;WP_Simple_HTML&#39;</span><span class="p">,</span> <span class="s1">&#39;WP_Simple_HTML_Templates&#39;</span> <span class="p">),</span></div><div class='line' id='LC112'><br/></div><div class='line' id='LC113'>		<span class="p">);</span></div><div class='line' id='LC114'><br/></div><div class='line' id='LC115'>	<span class="p">}</span></div><div class='line' id='LC116'><br/></div><div class='line' id='LC117'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">set_feed</span><span class="p">(){</span></div><div class='line' id='LC118'><br/></div><div class='line' id='LC119'>		<span class="nx">add_filter</span><span class="p">(</span> <span class="s1">&#39;available_template_engines&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="o">&amp;</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;add_feed&#39;</span> <span class="p">),</span> <span class="mi">1</span><span class="p">,</span><span class="mi">1</span> <span class="p">);</span></div><div class='line' id='LC120'><br/></div><div class='line' id='LC121'>	<span class="p">}</span></div><div class='line' id='LC122'><br/></div><div class='line' id='LC123'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">add_feed</span><span class="p">(</span> <span class="nv">$template_engines</span> <span class="p">){</span></div><div class='line' id='LC124'><br/></div><div class='line' id='LC125'>		<span class="nv">$feed_engines</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC126'><br/></div><div class='line' id='LC127'>			<span class="s1">&#39;rss&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span> <span class="s1">&#39;WP_Simple_HTML&#39;</span><span class="p">,</span> <span class="s1">&#39;WP_Simple_HTML_Templates&#39;</span> <span class="p">),</span></div><div class='line' id='LC128'>			<span class="s1">&#39;atom&#39;</span>	<span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span> <span class="s1">&#39;WP_Simple_HTML&#39;</span><span class="p">,</span> <span class="s1">&#39;WP_Simple_HTML_Templates&#39;</span> <span class="p">),</span></div><div class='line' id='LC129'><br/></div><div class='line' id='LC130'>		<span class="p">);</span></div><div class='line' id='LC131'><br/></div><div class='line' id='LC132'>		<span class="k">return</span> <span class="nb">array_merge</span><span class="p">(</span> <span class="nv">$template_engines</span><span class="p">,</span> <span class="nv">$feed_engines</span> <span class="p">);</span></div><div class='line' id='LC133'><br/></div><div class='line' id='LC134'>	<span class="p">}</span></div><div class='line' id='LC135'><br/></div><div class='line' id='LC136'><span class="p">}</span></div><div class='line' id='LC137'><br/></div><div class='line' id='LC138'><span class="nv">$html</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Template_Engine_Test</span><span class="p">();</span></div><div class='line' id='LC139'><span class="nv">$html</span><span class="o">-&gt;</span><span class="na">set_html</span><span class="p">();</span></div><div class='line' id='LC140'><span class="nv">$html</span><span class="o">-&gt;</span><span class="na">output</span><span class="p">(</span> <span class="s1">&#39;html&#39;</span> <span class="p">);</span></div><div class='line' id='LC141'><br/></div><div class='line' id='LC142'><span class="nv">$xml</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Template_Engine_Test</span><span class="p">();</span></div><div class='line' id='LC143'><span class="nv">$xml</span><span class="o">-&gt;</span><span class="na">set_xml</span><span class="p">();</span></div><div class='line' id='LC144'><span class="nv">$xml</span><span class="o">-&gt;</span><span class="na">output</span><span class="p">(</span> <span class="s1">&#39;xml&#39;</span> <span class="p">);</span></div><div class='line' id='LC145'><br/></div><div class='line' id='LC146'><span class="nv">$feed</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Template_Engine_Test</span><span class="p">();</span></div><div class='line' id='LC147'><span class="nv">$feed</span><span class="o">-&gt;</span><span class="na">set_feed</span><span class="p">();</span></div><div class='line' id='LC148'><span class="nv">$feed</span><span class="o">-&gt;</span><span class="na">output</span><span class="p">(</span> <span class="s1">&#39;rss&#39;</span> <span class="p">);</span></div><div class='line' id='LC149'><span class="nv">$feed</span><span class="o">-&gt;</span><span class="na">output</span><span class="p">(</span> <span class="s1">&#39;atom&#39;</span> <span class="p">);</span></div><div class='line' id='LC150'><br/></div><div class='line' id='LC151'><span class="nv">$bar</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Template_Engine_Test</span><span class="p">();</span></div><div class='line' id='LC152'><span class="nv">$bar</span><span class="o">-&gt;</span><span class="na">output</span><span class="p">(</span> <span class="s1">&#39;wallawalla&#39;</span> <span class="p">);</span></div><div class='line' id='LC153'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2795518/1c6995596a1dbb7903d0f093a462a516d94f4b71/file1.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2795518#file_file1.php" style="float:right;margin-right:10px;color:#666">file1.php</a>
            <a href="https://gist.github.com/2795518">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>
</div>
</div></p>
<p>Die Methode <code>get_template_engine()</code> erfüllt zwei Aufgaben. Zum einen gibt sie die angeforderte Template-Engine inkl. der dazugehörigen Template-Klasse zurück (sofern beides vorhanden). Und zum anderen speichert sie die Template-Engine und Template-Klasse in einer statischen Variablen. Welche Kombination aus Template-Engine und Template-Klasse zurück gegeben wird, wird anhand eines Tags entschieden. So ist dem Tag &#8216;html&#8217; schon einmal die Kombination <code>WP_Simple_HTML</code>/<code>WP_Simple_HTML_Templates</code> zugeordnet.<br />
Weitere Kombinationen aus Template-Engine und Template-Klasse lassen sich über einen Filter hinzufügen. Bevor geprüft wird ob es den angeforderten Tag mit der entsprechenden Kombination gibt, wird ein Filter ausgeführt der es uns erlaubt weitere Kombinationen hinzuzufügen.<br />
Dies nutzen die Methoden <code>set_xml()</code> und <code>set_feed()</code> aus. Sie fügen einfach dem Array der verfügbaren Template-Engines/Klassen ihre eigenen hinzu und machen diese dadurch zugänglich. Danach kann in der Methode <code>output()</code> auf die neu hinzugefügten Template-Engines und Klassen zugegriffen werden. Hiermit sind wir quasi am Ziel unserer Wünsche angelangt. Denn die Methode <code>output()</code> muss sich gar nicht mehr darum kümmern wie die Daten ausgegeben werden, sie stellt diese einfach bereit und erwartet das die entsprechende Template-Engine entsprechende Methoden zur Ausgabe zur Verfügung stellt.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-72/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress und FTP</title>
		<link>http://yoda.neun12.de/artikel-66</link>
		<comments>http://yoda.neun12.de/artikel-66#comments</comments>
		<pubDate>Sun, 20 May 2012 12:58:25 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=66</guid>
		<description><![CDATA[Über die Möglichkeiten seine FTP-Zugangsdaten an sein WordPress-Blog zu übermitteln und ein Plugin um dies relativ bequem zu machen.]]></description>
				<content:encoded><![CDATA[<p>Es gibt einige Möglichkeiten wie man seine FTP-Zugangsdaten an WordPress übermitteln kann. Aus einer kleinen <a title="Google+" href="https://plus.google.com/110569673423509816572/posts/dxFmeZa2Uue">Diskussion auf Google+</a> heraus kam die Idee diese mal mit ihren Vor- und Nachteilen zusammen zu fassen. Dies will ich im folgenden mal versuchen und dabei versuchen auch die Gründe aufzuzeigen warum WordPress manchmal FTP-Daten braucht und manchmal nicht. Nebenbei ist noch ein kleines Plugin entstanden welches eine weitere Möglichkeit bietet seine FTP-Daten in seien Blog zu hinterlegen.<span id="more-66"></span></p>
<h3>Ein paar Grundlagen</h3>
<p>Viele kennen es, wenn WordPress versucht Dateiaktionen auf den Server durchzuführen, z.B. ein Plugin oder Theme zu installieren, wird man nach seinen FTP-Zugangsdaten gefragt. Dabei ist es egal ob man eine Archivdatei direkt hochlädt oder von einen entfernten Server ein Update einspielt. Der Grund dafür liegt nicht bei WordPress, sondern bei Unix bzw. einem Unix-ähnlichen Betriebssystem wie Linux. Die Unix-ähnlichen Betriebssysteme kennen nämlich verschiedene Dateiberechtigungen und vor allem Dateibesitzer. Wenn unter einem Unix-System der (FTP-) <em>Benutzer A</em> eine Datei anlegt, so kann auch nur der <em>Benutzer A</em> diese Datei beschreiben bzw. löschen. <em>Benutzer A</em> ist der Dateibesitzer und hat die volle Kontrolle über die von ihm angelegte Datei. Möchte der <em>Benutzer A</em> das die Datei auch von anderen FTP-Benutzern gelesen bzw. beschrieben werden kann, so müssen entsprechende Berechtigungen vergeben werden. Es gibt drei Gruppen denen man den Zugriff auf die Datei erlauben kann: Nur der Eigentümer (also derjenige der die Datei angelegt hat), eine Gruppe von FTP-Benutzern und Alle anderen (<em>others</em> oder <em>public</em>). Zudem kann auch noch die Art des Zugriffs geregelt werden. Möglich sind: Lesen, Schreiben und Ausführen.<br />
<em>Benutzer A</em> könnte z.B. festlegen das die von ihm angelegte Datei nur von ihm beschrieben werden kann, jedoch z.B. alle FTP-Benutzer aus der Gruppe <em>Tastatursklaven</em> die Datei zumindest lesen können. Alle anderen FTP-Benutzer, so z.B. anonyme Gäste, können die Datei weder lesen noch sie beschreiben.</p>
<p>Dies ist ein grundlegender Schutz vor unberechtigten Zugriffen. Es wäre ja schon ein wenig fatal wenn jeder der will z.B. die Datei <em>wp-config.php</em> lesen, geschweige denn sie nach Lust&amp;Laune verändern könnte. Dieser &#8220;Schutz&#8221; wirkt sich nun nicht nur auf Dateien aus, sondern auch auf Verzeichnisse. Dateien die in einem Verzeichnis liegen das nur vom Eigentümer gelesen werden kann, können nicht von anderen FTP-Benutzern gelesen werden, egal welche Dateiberechtigung die einzelnen Dateien haben.<br />
Jetzt stellt sich natürlich die Frage was das alles mit WordPress zu tun hat. Schauen wir uns dazu erst einmal die &#8220;Ausnahme&#8221; an. Das wäre der Fall, wenn WordPress auf einem Server installiert ist auf dem PHP als so genanntes <em>php-cgi</em> läuft. Php-cgi ist eine ausführbare Datei und wird immer unter dem Konto des FTP-Benutzers ausgeführt. Wenn php-cgi also eine Datei auf den Server schreibt, hat die Datei als Eigentümer automatisch den FTP-Benutzer als Dateieigentümer.<br />
Der andere Fall, der weitaus verbreiteter ist, ist der, dass PHP als Apache-Modul (php_mod) ausgeführt wird. In diesen Fall ist nicht der FTP-Benutzer der Dateieigentümer, sondern der Apache-Server, der so gesehen auch ein FTP-Benutzer ist, jedoch mit einen anderen Benutzernamen. Je nachdem wie der Server konfiguriert ist, variiert der FTP-Benutzername des Servers. Häufig ist dies &#8220;nobody&#8221;, aber auch &#8220;wwwrun&#8221;, &#8220;PHP-User&#8221; oder schlicht &#8220;PHP&#8221;. Welcher Benutzername der Apache-Server genau hat, muss also im Einzelfall jeder selber heraus finden, ist aber für das Verständnis gar nicht so wichtig.</p>
<p>Halten wir kurz fest:</p>
<ul>
<li>Unter Unix und Unix-ähnlichen Betriebssystemen haben Dateien und Verzeichnisse einen Besitzer</li>
<li>Nicht jeder FTP-Benutzer kann auf alle Dateien und Verzeichnisse zugreifen</li>
<li>Die Zugriffe auf Dateien und Verzeichnisse werden durch Zugriffsrechte geregelt</li>
<li>Läuft PHP als Apache-Modul, so hat der Server einen eigenen &#8220;Benutzernamen&#8221; und hat dementsprechend auch Besitzrechte an den von ihm angelegten Dateien und Verzeichnisse.</li>
</ul>
<h3>Was passiert beim Upload?</h3>
<p>Wenn wir nun versuchen Dateien oder Verzeichnisse in unser Blog hochzuladen, dann muss WordPress erst einmal feststellen wie es dies bewerkstelligen kann. Die einfachste Methode wäre natürlich WordPress würde die Daten einfach mal so auf dem Server ablegen. Genau genommen versucht WordPress dies auch, lässt es aber meistens aus Sicherheitsgründen sein. Um festzustellen ob WordPress die Daten direkt auf den Server speichern kann, schreibt es eine kleine Testdatei. Danach prüft WordPress ob der FTP-Benutzer mit dem Dateibesitzer der Testdatei übereinstimmt.<br />
Im Fall von php-cgi stimmen die Besitzrechte überein und WordPress kann ohne weitere Nachfrage weiter machen.<br />
Im Fall von PHP als Modul hat die Testdatei jedoch den Server als Besitzer und nicht den FTP-Benutzer. WordPress entscheidet sich in diesen Fall dagegen die Daten direkt auf den Server zu schreiben und sucht nach einer anderen Methode.</p>
<p>Natürlich stellt sich jetzt die Frage warum WordPress nicht doch einfach die Daten auf den Server ablegt. Der Grund liegt in den Zugriffsrechten und und einer Sicherheitslücke die sich (theoretisch) daraus ergibt.<br />
Stellen wir uns mal vor <em>Benutzer A</em> und <em>Benutzer B</em> haben auf dem <em>Webserver C</em> jeweils einen Webspace gemietet. Alle Dateien und Verzeichnisse die von <em>Benutzer A</em> angelegt werden, können auch nur von <em>Benutzer A</em> gelesen und beschrieben werden. Genauso verhält es sich mit den Dateien und Verzeichnissen von <em>Benutzer B</em>. Was ist aber mit den Dateien und Verzeichnissen von &#8220;<em>Benutzer C</em>&#8220;? <em>Benutzer C</em> wäre in diesem Fall der Webserver. Und wenn der Webserver im Webspace von <em>Benutzer A</em> eine Datei anlegt, dann ist der Webserver der Besitzer dieser Datei. Da sich <em>Benutzer A</em> und <em>B</em> einen Webserver teilen, hat <em>Benutzer B</em> theoretisch über den Webserver Zugriff auf die Dateien im Webspace von <em>Benutzer A</em>, die vom Webserver angelegt wurden. Theoretisch könnte <em>Benutzer B</em> also den Webserver anweisen Dateien die er im Webspace von <em>Benutzer A</em> angelegt hat, auszulesen und im Webspace von <em>Benutzer B</em> zu kopieren. Möglich wäre dies, da der Webserver als Dateibesitzer ja die nötigen Rechte dazu hat.<br />
Dies ist eine eher theoretische Sicherheitslücke. Theoretisch deshalb, da auf einen vernünftig konfigurierten Server so etwas nicht möglich sein sollte. Der Server sollte so konfiguriert sein, dass kein Benutzer auf Dateien oder Verzeichnisse außerhalb seines Webspaces zugreifen kann. Andernfalls könnte jeder Benutzer auch problemlos die Konfigurationsdateien des Webservers manipulieren.<br />
Doch grau ist alle Theorie, die Praxis hat schon andere Fälle hervor gebracht. Und genau deswegen geht WordPress auf Nummer sicher und zieht es vor die Daten nicht direkt auf den Server abzulegen sofern es unter PHP als Apache-Modul läuft.</p>
<p>Kurz zusammen gefasst: Stellt WordPress fest das es nicht mit den gleichen Benutzernamen wie der FTP-Benutzer läuft, weigert es sich schlichtweg die Daten direkt auf den Server zu schreiben um nicht eine Sicherheitslücke bei den Zugriffsberechtigungen zu öffnen.</p>
<h3>Und es nervt doch</h3>
<p>Da PHP als Apache-Modul sehr verbreitet ist, werden auch viele WordPress-Nutzer regelmäßig nach ihren FTP-Zugangsdaten gefragt. FTP ist die zweite Variante mit der WordPress die Daten auf den Server bekommt. Dazu tut WordPress einfach so, als sei es der FTP-Benutzer unter dem der Blog angelegt wurde und der rechtmäßig Zugriff auf alle Dateien hat. Somit werden die Daten nicht mit dem FTP-Benutzer &#8220;nobody&#8221; (oder PHP-Run, oder PHP, oder oder oder) geschrieben, sondern mit dem von uns angegebenen Benutzernamen und Passwort.</p>
<p>Wer nur selten Daten auf seinen Server kopiert, den wird es nicht stören ab und an mal seine FTP-Daten anzugeben. Es gibt aber so einige Fälle wo es doch sehr nervig werden kann. Als Entwickler schiebt man ständig zu Testzwecken Daten auf den Server und löscht sie dort wieder. Das man auch beim Löschen von Daten die FTP-Zugangsdaten angeben muss, dürfte mittlerweile klar sein. Denn WordPress kann aufgrund der fehlenden Zugriffsberechtigungen nicht als Benutzer &#8220;PHP-Run&#8221; auf die Daten von FTP-Benutzer &#8220;Horst4711&#8243; zugreifen.<br />
Manchmal ist es aber auch einfach zu unbequem die FTP-Zugangsdaten, die sich natürlich kaum einer merkt weil das Passwort kryptisch³ ist, raus zu suchen. Und manchmal hat ausgerechnet der Mitarbeiter die FTP-Zugangsdaten, der gerade in Kambodscha Urlaub macht und nicht erreichbar ist.</p>
<p>Für solche Fälle gibt es einige Möglichkeiten die FTP-Zugangsdaten direkt in WordPress zu hinterlegen. Alle Möglichkeiten haben ihre Vor- und Nachteile. Einige sind sogar, m.E. nach, ein wenig gefährlich.</p>
<h3>Speichern ist einfacher als raus suchen</h3>
<p>Ab Werk kann man WordPress seine FTP-Daten also nur per Formular übergeben. Wer häufig unterwegs ist und sich dementsprechend genauso häufig in ungesicherte WLAN-Netzwerke einloggt (oder einloggen muss), wird immer ein mulmiges Gefühl haben wenn er die FTP-Zugangsdaten ungesichert quer durch die Bahnhofshalle zum Router schicken muss. Gerade wenn es um Kunden-Server geht ist hier etwas Vorsicht geboten. Falls jetzt jemand die Idee hat SSL wäre ja eine Lösung, der hat im Prinzip Recht. Jedoch denken die meisten Kunden da ganz anders. Wenn SSL extra kostet, dann braucht man das nicht. So denken zumindest die meisten Kunden.<br />
Diese Lösung hat allerdings auch einen Vorteil. Legt man auf dem Server für jeden Admin einen FTP-Konto an, so kann <em>Admin B</em> nicht aus versehen (oder mit voller Absicht) die Dateien und Verzeichnisse von <em>Admin A</em> löschen oder überschreiben.</p>
<h4>wp-config</h4>
<p>Die erste Alternative zur Übertragung via Formular wäre die FTP-Zugangsdaten in der <code>wp-config.php</code> abzulegen. Dazu stellt WordPress drei Konstanten bereit: <code>FTP_HOST</code>, <code>FTP_USER</code> und <code>FTP_PASS</code>. Die Namen sprechen für sich und es dürfte klar sein für was die einzelnen Konstanten stehen. Wer mehr über die Konstanten von WordPress erfahren will, sollte mal einen Blick bei <a title="Google+ Profil von Dominik Schilling" href="https://plus.google.com/101675293278434581718">Dominik Schilling</a> (G+ Profil)  rein werfen. Er hat eine <a title="WP-Grafie, WP-Konstanten" href="http://wpgrafie.de/195/wordpress-konstanten/#dateisystem">umfangreiche Auflistung alle Konstanten</a>.<br />
Beispiel:</p>
<pre class="brush:php">// defining the ftp-account data
if( ! defined( 'FTP_HOST' ) )
  define( 'FTP_HOST', 'meine-dmoain.de' );

if( ! defined( 'FTP_USER' ) )
  define ( 'FTP_USER', 'Horst4711' );

if( ! defined( 'FTP_PASS' ) )
  define( FTP_PASS', '&amp;SchmierMaxe$0815!' );</pre>
<p>Man sieht schon wo hier das Problem liegen könnte. Das Passwort liegt unverschlüsselt in einer PHP-Datei von der so ziemlich jeder Hacker weiß das dort was zu holen ist. Auf der anderen Seite steht in der selben Datei aber auch das Passwort für die Datenbank, ebenfalls unverschlüsselt. Man kann jetzt so oder so argumentieren. Die einen sind der Meinung das eh Hopfen&amp;Malz verloren ist wenn die <code>wp-config.php</code> geknackt ist. Die anderen sagen das man einen Hacker nicht noch freien FTP-Zugang zum Server geben muss wenn er sich schon die Datenbank unter den Nagel gerissen hat.<br />
Diese Methode spart auf alle Fälle die lästige Tipparbeit und man muss, sofern man mit mehren Mitarbeitern an einen Blog arbeitet, die FTP-Zugangsdaten nicht jedem aufs Auge drücken. Irgendwo gibt es immer einen der Zugangsdaten auf einen PostIt schreibt und diesen dann irgendwo vergisst oder verliert.<br />
Hier wird deutlich das WordPress eigentlich ein Single-User-System ist dem man notdürftig eine Benutzerverwaltung aufgepflanzt hat. Denn so kann jeder Benutzer mit Admin-Rechten auf alle Dateien und Verzeichnisse zugreifen. Ob nun gewollt oder ungewollt. Es ist durchaus denkbar das <em>Admin B</em> ein Verzeichnis löscht oder überschreibt das von <em>Admin A</em> angelegt wurde und das auf gar keinen Fall gelöscht oder überschrieben werden durfte.</p>
<h4>FS_METHOD</h4>
<p>Eine andere Alternative zum Formular besteht darin, WordPress dazu zu zwingen die Daten direkt auf den Server zu schreiben. Indem man die Konstante <code>FS_METHOD</code> auf <code>direct</code> setzt, lässt man WordPress erst gar nicht dazu kommen auszuwählen ob es die Daten direkt oder per FTP auf den Server schreibt.<br />
In meinen Augen ein Fauxpas sofern man dies auf einen Shared-Host anwendet. Für die möglichen Gefahren, siehe oben.<br />
Nun ist aber nicht jedes Blog auf einen Shared-Host angelegt. Wer stolzer Besitzer eines eigenen Servers ist, kann diese Methode durchaus dazu nutzen den FTP-Upload zu umgehen. Wenn es auf dem ganzen Server nur einen FTP-Benutzer gibt, gibt es auch keinen zweiten der unberechtigt auf die Dateien zugreifen kann. Wobei man diese Aussage ein wenig relativieren muss. Denn als Apache-Modul ausgeführt, ist der Webserver natürlich auch ein FTP-Benutzer. Durch setzen der Konstante <code>FS_METHOD</code> auf <code>direct</code>, werden alle Dateien und Verzeichnisse mit dem Besitzer &#8220;Webserver&#8221; (oder so ähnlich) erstellt. Das kann in sofern problematisch werden, wenn in einem Plugin oder Theme &#8220;versehentlich&#8221; etwas wie <code>exec( "rm -rf /" );</code> drin steht. Dann werden halt mal alle Dateien und Verzeichnisse die vom aktuellen Benutzer, sprich Webserver, angelegt wurden, gelöscht. Ohne Rückfrage versteht sich. Aber auch ein <code>unlink()</code> kann an der falschen Stelle mit dem falschen Pfad ausreichend Schaden anrichten.<br />
Ein weiteres Problem dürfte zwar nur selten auftreten, ist mir persönlich aber durchaus schon mal passiert. Daten die vom Webserver geschrieben worden sind konnte ich als FTP-Benutzer nicht mehr löschen. Sicherlich auch eine Sache der Server-Konfiguration, man sollte aber vor Einsatz dieser Alternative erst einmal ausprobieren ob man denn die Daten die der Webserver geschrieben hat auch wieder löschen kann. Das Selektive Löschen einzelner Dateien oder Verzeichnisse kann sich dann sehr mühsam gestalten.</p>
<p>Die Konstante <code>FS_METHOD</code> kann jedoch auch sehr nützlich sein. Wir erinnern uns an php-cgi bei dem alle Daten unter dem Benutzernamen geschrieben werden. WordPress muss unter php-cgi nicht nachfragen ob es die Methode &#8220;<em>direct</em>&#8221; oder &#8220;<em>ftp</em>&#8221; benutzen soll, dadurch kann es gleich loslegen. Das ist vielleicht aber gar nicht immer erwünscht. Mit <code>FS_METHOD</code> können wir WordPress auch dazu zwingen FTP als Methode zu verwenden und jedes mal Benutzernamen und Passwort (und Hostname) abzufragen. Für Blogs die unter php-cgi laufen also ein kleiner Workaround wie man ganz einfach eine Authentifizierung vor dem Schreiben/Löschen von Daten erzwingen kann.</p>
<pre class="brush:php">/*
 * force users to use ftp
 *
 * use 'ftpext' for using Apache FTP Extension
 * use 'ftpsockets' for ftp-sockets (Socket-Class or PHP-Extension)
 *
 */

if( ! defined( 'FS_METHOD' ) )
  define( 'FS_METHOD', 'ftpext' );</pre>
<p>Obwohl ich hier nur mehr oder minder die Oberfläche angekratzt habe, sieht man schon wie komplex die gesamte Thematik ist. Gänzlich ausgeklammert habe ich z.B. die ganze Thematik um SSH/SSL, SFTP und das WordPress-Filesystem sowieso. Das ist genug Stoff für weitere drei oder vier Artikel.</p>
<h3>Eine Plugin-Lösung</h3>
<p>Meiner Philosophie zur Folge muss ein Blogartikel nicht komplett und vollständig sein, sondern vielmehr zum Nachdenken, Experimentieren und vor allem Diskutieren anregen. Sicherlich gibt es den einen oder anderen Aspekt den ich übersehen, falsch oder missverständlich dargestellt habe oder der mehr Interesse geweckt hat als der Artikel bedienen kann.<br />
Genauso halte ich es auch mit meinen Plugins. Keine fertige Allroundlösung mit allen erdenklichen Features, sondern ein Lösungsansatz für ein Problem. Das folgende Plugin liegt in der Version <strong>0.1</strong> vor, ist also der erste Entwurf und eher ein PoC (Proof of Concept). Sowohl Artikel als auch Plugin sind beide an einen Tag entstanden, dementsprechend unausgiebig wurde das Plugin von mir getestet. Ich bitte dies beim Ausprobieren zu berücksichtigen.</p>
<h4>FTP-Account to UserMeta</h4>
<p>Dieses Plugin macht in etwa genau das was der (uninspiriert gewählte) Name aussagt. Es speichert die FTP-Zugangsdaten in den Benutzer-Daten des jeweiligen Benutzers. Damit sind zwei Anforderungen die im Laufe des Artikels aufgetaucht sind erledigt. Zum einen, und wichtigsten, muss man die FTP-Zugangsdaten nicht mehr ständig eintippen. Einmal im Benutzerprofil eingegeben stehen sie ab dann immer zur Verfügung wenn WordPress diese anfordert. Zum anderen können mehrere FTP-Benutzer ihre Daten hinterlegen da für jeden Benutzer separate FTP-Zugangsdaten gespeichert werden. Dadurch kann z.B. verhindert werden das <em>Benutzer A</em> die Dateien von <em>Benutzer B</em> versehentlich löscht, das Plugin kommt also aus der Ecke &#8220;Betreutes Bloggen&#8221;.</p>
<p>Das Plugin hinkt noch an der einen oder anderen Stelle. So werden die FTP-Passwörter (noch) im Klartext in der Datenbank gespeichert. Es sollte aber kein Problem darstellen eine entsprechende Ver- und Entschlüsselung nachzurüsten.<br />
Auch fehlt noch die Unterstützung für SFTP/SSL da mir hierfür derzeit die nötige Umgebung fehlt (ja, ich habe weder SSL noch sonst was auf meinen lokalen Server. Nicht mal mod_ftp). Gegen Bereitstellung von etwas Platz auf einen entsprechenden Server mit SSL/SFTP bin ich gerne bereit das zu ändern <img src="http://yoda.neun12.de/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" />  Ansonsten muss man halt warten bis ich das auf meinem lokalen Server nachgerüstet habe.</p>
<p>Die Bedienung des Plugins ist recht simple. Nach der Installation und Aktivierung werden im Benutzerprofil drei Felder für FTP-Hostname, FTP-Benutzername und FTP-Passwort hinzugefügt. Sind die drei Felder ausgefüllt und wird das Profil aktualisiert, verschwindet ab diesen Zeitpunkt für diesen Benutzer die Aufforderung seine FTP-Zugangsdaten einzugeben sofern diese erforderlich sind. Alle anderen Benutzer die FTP benutzen können aber keine Zugangsdaten hinterlegt haben, werden weiterhin aufgefordert ihre Zugangsdaten bei Bedarf einzugeben.<br />
<a class="highslide" onclick="return hs.expand(this)"  href="http://yoda.neun12.de/wp-content/uploads/2012/05/ftp2um.png"><img class="alignleft size-thumbnail wp-image-68" title="FTP2UM im Backend" src="http://yoda.neun12.de/wp-content/uploads/2012/05/ftp2um-150x150.png" alt="" width="150" height="150" /></a>Die Kontrolle darüber welche Benutzer überhaupt die drei Felder zu sehen bekommen, erfolgt hardcodiert im Plugin selber. Voreingestellt sind Administratoren (Role administrator), was wahrscheinlich am meisten Sinn macht. Es können aber auch andere Rollen bzw. Fähigkeiten (Capabilities) angegeben werden, womit eine recht feine Einstellung möglich ist wer seine FTP-Zugangsdaten eingeben kann.<br />
Damit nach der Deinstallation des Plugins keine sensiblen Daten in der Datenbank verbleiben, werden diese bei der Deinstallation (nicht deaktivierung!) gelöscht. Sofern man das Plugin also über das Backend entfernt, kann man es gefahrlos ausprobieren.</p>
<p>Das Plugin findet ihr auf <a title="FTP2UM on GitHub" href="https://github.com/RalfAlbert/WordPress-FTP-Account-to-UserMeta">Github</a>. Für diejenigen die es nicht so mit Git haben, als <a title="FTP 2 UM V0.1.2" href="https://github.com/downloads/RalfAlbert/WordPress-FTP-Account-to-UserMeta/WordPress-FTP-Account-to-UserMeta_0.1.2.zip">Zip-Datei</a>. Die anderen dürfen gerne <a title="Fork FTP2UM" href="https://github.com/RalfAlbert/WordPress-FTP-Account-to-UserMeta/fork">forken</a>, pullen und commiten.</p>
<h4>Update</h4>
<p>Da das Plugin auf  einiges Interesse gestoßen ist, habe ich den Code mal etwas bereinigt und glatt gezogen. Neue Funktionen sind in <a title="Version 0.1.2" href="https://github.com/downloads/RalfAlbert/WordPress-FTP-Account-to-UserMeta/WordPress-FTP-Account-to-UserMeta_0.1.2.zip">dieser Version (v0.1.2)</a> nicht hinzugekommen, dafür aber die eine oder andere Funktion für die nächsten Versionen notiert. Es wird sicherlich noch das eine oder andere umgesetzt werden, da ich hierdurch auch die Möglichkeit bekomme verschiedene Sachen auszuprobieren und ggf. darüber etwas zu schreiben.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-66/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Quick-Tipp: Schnell-Login</title>
		<link>http://yoda.neun12.de/artikel-52</link>
		<comments>http://yoda.neun12.de/artikel-52#comments</comments>
		<pubDate>Sun, 15 Jan 2012 05:34:41 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Code-Snippets]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=52</guid>
		<description><![CDATA[Hin und wieder gibt es das Bedürfnis sich ohne Angabe von Benutzername und Passwort einzuloggen. Das könnte z.B. der Fall sein wenn man automatisierte Tests durchführen möchte die einen Login benötigen. Oder aber man muss zum Testen immer wieder den Benutzer wechseln um verschiedene Szenarien durchzuspielen. Aber auch wenn man sich in einer eher &#8220;unsicheren [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Hin und wieder gibt es das Bedürfnis sich ohne Angabe von Benutzername und Passwort einzuloggen. Das könnte z.B. der Fall sein wenn man automatisierte Tests durchführen möchte die einen Login benötigen. Oder aber man muss zum Testen immer wieder den Benutzer wechseln um verschiedene Szenarien durchzuspielen.<br />
Aber auch wenn man sich in einer eher &#8220;unsicheren Umgebung&#8221; befindet (z.B. schlecht abgesichertes öffentliches Netzwerk) möchte man vielleicht nicht so gerne seine Login-Daten eintippen. Es wäre also ganz praktisch wenn man sich (automatisiert) einloggen kann ohne ständig Login-Daten einzutippen.</p>
<p>WordPress lässt sich relativ einfach dazu bringen Login-Daten automatisiert anzunehmen. In erster Linie ist die Funktion <code>wp_signon()</code> dafür zuständig den Login durchzuführen. Dazu übergibt man ihr Benutzername und Passwort, die Funktion gibt daraufhin <code>true</code> zurück bzw. ein WordPress-Fehler-Objekt. Dies kann man bequem mit <code>is_wp_error()</code> abfragen und so mit nur wenigen Zeilen einen Login durchführen.<div class="gistem"><div id="gist-1529022" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;?php</span></div><div class='line' id='LC2'><span class="k">require</span><span class="p">(</span> <span class="nb">dirname</span><span class="p">(</span><span class="k">__FILE__</span><span class="p">)</span> <span class="o">.</span> <span class="s1">&#39;/wp-load.php&#39;</span> <span class="p">);</span></div><div class='line' id='LC3'><span class="nx">is_wp_error</span><span class="p">(</span></div><div class='line' id='LC4'>	<span class="nx">wp_signon</span><span class="p">(</span></div><div class='line' id='LC5'>		  <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC6'>			<span class="s1">&#39;user_login&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;YourLoginName&#39;</span><span class="p">,</span></div><div class='line' id='LC7'>			<span class="s1">&#39;user_password&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;YoUrAw3s0M3P455W0rD&#39;</span></div><div class='line' id='LC8'>			<span class="p">)</span> </div><div class='line' id='LC9'>		 <span class="p">)</span></div><div class='line' id='LC10'><span class="p">)</span> <span class="o">?</span> <span class="k">die</span><span class="p">(</span><span class="s1">&#39;Mooo... :(&#39;</span><span class="p">)</span> <span class="o">:</span> <span class="nx">wp_safe_redirect</span><span class="p">(</span> <span class="nx">admin_url</span><span class="p">()</span> <span class="p">);</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1529022/41c58a4015161cb71154d1fdb3f0c91a4da5d0e1/quicklogin.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1529022#file_quicklogin.php" style="float:right;margin-right:10px;color:#666">quicklogin.php</a>
            <a href="https://gist.github.com/1529022">This Gist</a> is brought to you using <a href="http://en.bainternet.info/2011/simple-gist-embed"><small>Simple Gist Embed</small></a>.
          </div>
        </div>
</div>
</div></p>
<p>Speichert man den Code in einer separaten Datei ab, so muss zuerst <code>wp-load.php</code> eingebunden werden um die WordPress-Funktionen verfügbar zu machen. Hier bitte auf den Pfad achten, iom Gist liegt die Datei im gleichen Verzeichnis wie <code>wp-load.php</code>.  Danach wird direkt <code>wp_signon()</code> mit einem Array aus Benutzername und Passwort gefüttert, welches wiederum direkt als Parameter an <code>is_wp_error()</code> übergeben wird.<br />
Durch Aufruf der Datei ist man direkt eingeloggt und wird ins Backend umgeleitet. Möchte man lieber ins Frontend umgeleitet werden, so ersetzt man einfach <code>admin_url()</code> durch <code>site_url()</code>.</p>
<p>Ich habe mir für meine Entwicklungsarbeit ein kleines Plugin geschrieben mit dem ich recht schnell zwischen verschiedenen Benutzern hin- und her wechseln kann. Dazu listet mir das Plugin auf der Login-Seite die Test-User auf, welche ich zuvor angelegt habe. Durch einen Klick auf einen entsprechenden User-Namen kann ich mich dann ohne Eingabe von Benutzername und Passwort anmelden. Auf Optik habe ich verzichtet, da es ein Werkzeug bei der Entwicklung ist. Wer mag, kann dem ganzen ja noch ein bisschen optischen Feinschliff verpassen.<br />
Das Plugin <a title="WP-Quicklogin" href="https://github.com/RalfAlbert/WP-Quicklogin">WP-Quicklogin</a> ist auf Github zu finden.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-52/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
