<?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; Allgemein</title>
	<atom:link href="http://yoda.neun12.de/artikel-category/allgemein/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>Das Ende einer langen Pause</title>
		<link>http://yoda.neun12.de/artikel-125</link>
		<comments>http://yoda.neun12.de/artikel-125#comments</comments>
		<pubDate>Wed, 20 Nov 2013 16:27:29 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=125</guid>
		<description><![CDATA[Seit rund 6 Monaten habe ich nun hier nichts mehr rein geschrieben. In etwa genauso lange hat man von mir generell nichts mehr in Sachen WordPress lesen können. Anfangs war es schlichtweg Zeitmangel aufgrund von mehreren Projekten die ich begonnen hatte. Aber dann kam irgendwann der Tag an dem gar nichts mehr ging. Von jetzt [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Seit rund 6 Monaten habe ich nun hier nichts mehr rein geschrieben. In etwa genauso lange hat man von mir generell nichts mehr in Sachen WordPress lesen können. Anfangs war es schlichtweg Zeitmangel aufgrund von mehreren Projekten die ich begonnen hatte. Aber dann kam irgendwann der Tag an dem gar nichts mehr ging. Von jetzt auf Gleich ein Balken im Kopf der die totale Blockade ausgelöst hatte. Nicht eine Zeile Code habe ich mehr hin bekommen. Nicht einen klaren Gedanken fassen können wenn ich ein Problem lösen wollte. Nichts ging mehr.</p>
<p>Zum Glück ist das Programmieren nicht meine Lebensgrundlage, ansonsten wäre ich einige Monate lang berufsunfähig gewesen. So konnte ich mir den Luxus einer langen Auszeit gönnen und habe mich rein gar nicht mehr mit WordPress, PHP oder verwandten Themen befasst.<br />
Andere Dinge rückten in den Mittelpunkt und das war gut so. Ich habe den nötigen Abstand gewonnen um wieder klar denken zu können. Den nötigen Abstand um Projekte, die ich begonnen hatte, wieder mit der nötigen Distanz und Kritikfähigkeit betrachten zu können. Abstand der es mir ermöglichte Wichtiges von Unwichtigen zu trennen.</p>
<p>Bei vielen reicht dafür ein Urlaub. Urlaub bedeutet bei mir jedoch oftmals Zeit zum Coden. Und das war dann wohl auch der Fehler den ich gemacht hatte, weswegen meine Auszeit etwas länger ausfiel.<br />
Dadurch das ich mir Zeit für mich selber und andere Dinge genommen hatte &#8211; eher nehmen musste &#8211; konnte ich den Akku wieder aufladen. Jetzt macht mir das Coden wieder Spaß, die Zeilen flutschen nur so auf den Bildschirm und ich verbeisse mich nicht mehr an unwichtigen Details.<br />
Viele Projekte habe ich auf Eis gelegt oder gleich ganz aufgegeben, ansonsten hätte ich das Coden wahrscheinlich aus lauter Frust komplett an den Nagel gehängt.<br />
Zu den jetzigen Neuanfang gehört auch ein kleines Plugin, welches ich ohne durch Altlasten aufgehalten zu werden, in den letzten vier Tagen umgesetzt habe.</p>
<p>Entstanden ist das Plugin durch die Beschäftigung mit Menschen die es schon als große Leistung ansehen Facebook zu starten. Denn es kann durchaus hinderlich sein wenn man ständig von Menschen umgeben ist die sich auskennen. Man verliert sehr schnell den Blick dafür was diejenigen benötigen (könnten), die sich weniger bis gar nicht auskennen. Raus aus den Elefenbeinturm ist hier die Devise gewesen.<br />
Und so ist in den letzten Tagen <a title="PwCR on GitHub" href="https://github.com/RalfAlbert/PasswordChangeReminder">Password Change Reminder (PwCR)</a> entstanden. Bei einer schnellen Recherche konnte ich kein Plugin finden welches den Benutzer daran erinnert regelmäßig sein Passwort zu ändern. PwCR macht genau das. Nicht mehr und nicht weniger.<br />
Das Plugin ist, wie so häufig bei mir, in einem sehr früher Stadium. Da können noch ein paar Fehler dabei sein, die (englischen) Texte sind alle noch etwas schief, Übersetzungsarbeit ist zu leisten und natürlich Dokumentation. Genaueres zu dem Plugin werde ich in eine  seperaten Artikel schreiben. Bis dahin kann jeder der möchte es schon mal ausprobieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-125/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Trenne Ausgabe und Logik</title>
		<link>http://yoda.neun12.de/artikel-65</link>
		<comments>http://yoda.neun12.de/artikel-65#comments</comments>
		<pubDate>Sun, 01 Apr 2012 02:23:02 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=65</guid>
		<description><![CDATA[Die Trennung von Ausgabe und Logik ist ein Grundsatz den man beherzigen sollte. Viele CMS bieten Template-Engines an um dies zu erreichen. WordPress bildet in diesen Punkt eine Ausnahme. Es bringt keine eigene Template-Engine mit, sondern setzt auf PHP als Template-Engine. Grundsätzlich keine schlechte Idee, denn PHP ist ursprünglich eine Template-Sprache. Aber die Möglichkeiten die [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Die Trennung von Ausgabe und Logik ist ein Grundsatz den man beherzigen sollte. Viele CMS bieten Template-Engines an um dies zu erreichen. WordPress bildet in diesen Punkt eine Ausnahme. Es bringt keine eigene Template-Engine mit, sondern setzt auf PHP als Template-Engine. Grundsätzlich keine schlechte Idee, denn PHP ist ursprünglich eine Template-Sprache. Aber die Möglichkeiten die PHP bietet führen grundsätzlich dazu das Logik und Ausgabe munter miteinander vermischt werden, was wiederum zu arg unleserlichen und vor allem unflexiblen Code führt.<br />
In diesen Artikel geht es nicht ausschließlich um WordPress. Das hier gezeigte lässt sich aber sehr leicht auf WordPress anpassen und viele WordPress-Themes haben es bitter nötig sich hinter der Fassade auch mal etwas aufzuhübschen.</p>
<p>Ich hatte in meinem letzten Artikel bereits kurz angesprochen das es eine gute Idee ist Logik und Ausgabe voneinander zu trennen. Eins der besten Argumente für die Trennung von Logik und Ausgabe ist wohl das, dass man einmal geschriebenen Code erneut verwenden kann, man spart sich so viel Arbeit. Aber auch die Übersichtlichkeit steigt, was z.B. die Fehlersuche vereinfacht. Und nicht zu Letzt ist es leichter im Team zu arbeiten. So kann sich einer um die Logik kümmern während der andere sich ausschließlich um das Design (Ausgabe) kümmert.</p>
<p>Das hier im Artikel beschriebene dürfte für PHP-Profis nichts neues sein. Für den einen oder anderen meiner Leser (sofern vorhanden <img src="http://yoda.neun12.de/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" />  ) kann es aber durchaus interessant sein. Fangen wir also mal ganz locker an.</p>
<h3>Ran an den Speck</h3>
<p>Ziel soll es sein das in der einen Datei ein Template (Vorlage) steht, während in einer anderen Datei dieses Template mit Daten gefüllt wird. Will man die Trennung noch weiter voran treiben, so legt man weitere Dateien an die sich z.B. um die Beschaffung der Daten kümmern oder um die Ausgabe der Daten (HTML, RSS, Druck, usw.).<br />
Wir wollen uns aber mal darauf beschränken ein einfaches  Template mit einem ebenso einfachem Array zu füllen. Bei dem Template soll es sich um eine Liste handeln. In WordPress zum Beispiel ist fast alles eine Liste. Ob nun die Artikel auf der Startseite, die Blogroll, eine Aufzählung der letzten Kommentare, Menüs, usw. Eigentlich besteht WordPress zu gefühlten 90% aus Listen. Listen dürften als Beispiel also ganz brauchbar sein.<br />
Schauen wir uns mal kurz die Anatomie einer Liste in HTML an. Sie besteht im Prinzip aus zwei Teilen. Den äußeren Teil der die Liste definiert und den inneren Teil der die eigentliche Liste darstellt. In <a title="W3Schools" href="http://www.w3schools.com/html/html_lists.asp">HTML gibt es drei native Listentypen</a>: &lt;ol&gt; (Ordered List), &lt;ul&gt; (Unordered List) und &lt;dl&gt; (Defenition List). Da die &lt;dl&gt;-Liste etwas komplexer ist nehmen wir uns mal die &lt;ol&gt;- und &lt;ul&gt;-Listen vor. Als &#8220;Ersatz&#8221; für die &lt;dl&gt;-Liste möchte ich eine einfache &lt;div&gt;-Liste dazu nehmen. Die &lt;div&gt;-Liste besteht aus einem äußeren Div-Container und &lt;p&gt;-Tags für die Listenelemente.</p>
<p>Nachdem nun klar ist um welche Listen es geht, brauchen wir noch ein paar Daten für die Liste. Die meisten Daten werden uns als Array oder Objekt zur Verfügung gestellt. Wir können ja mal annehmen wir hätten gerade eine Datenbankabfrage gemacht und wollten nun das Ergebnis als Liste darstellen. Der Einfachheit halber begnügen wir uns mit einem eindimensionalen Array das lediglich die Zahlwörter von Eins bis Drei enthält.</p>
<h3>Aus Daten wird eine Liste</h3>
<p>Das Problem ein Array als Liste darzustellen ist recht einfach zu lösen. Äußeren Teil ausgeben, das Array durchlaufen und den inneren Teil erstellen, inneren Teil in den Äußern Teil einbetten und fertig ist die Liste.<br />
<div class="gistem"><div id="gist-2138338" 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_once</span> <span class="s1">&#39;class-formatter.php&#39;</span><span class="p">;</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="k">class</span> <span class="nc">Lister</span> <span class="k">extends</span> <span class="nx">Formatter</span></div><div class='line' id='LC5'><span class="p">{</span>	</div><div class='line' id='LC6'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_list</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='LC7'><br/></div><div class='line' id='LC8'>		<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='LC9'>		<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='LC10'><br/></div><div class='line' id='LC11'>		<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='LC12'><br/></div><div class='line' id='LC13'>			<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='LC14'>			<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='LC15'><br/></div><div class='line' id='LC16'>			<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="s1">&#39;&lt;li&gt;%item%&#39;</span><span class="p">,</span> <span class="nv">$values</span> <span class="p">);</span></div><div class='line' id='LC17'>		<span class="p">}</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>		<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="s1">&#39;&lt;ol&gt;%inner%&lt;/ol&gt;&#39;</span><span class="p">,</span> <span class="nv">$inner</span> <span class="p">);</span></div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'>	<span class="p">}</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'><span class="p">}</span></div><div class='line' id='LC24'><br/></div><div class='line' id='LC25'><span class="nv">$data</span> <span class="o">=</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></div><div class='line' id='LC26'><br/></div><div class='line' id='LC27'><span class="nv">$list</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Lister</span><span class="p">();</span></div><div class='line' id='LC28'><span class="k">echo</span> <span class="nv">$list</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC29'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2138338/9fea73567c4999306d4ea4cc949fec8a2d223e8a/file1.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2138338#file_file1.php" style="float:right;margin-right:10px;color:#666">file1.php</a>
            <a href="https://gist.github.com/2138338">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><br />
Um es gleich ordentlich zu machen verwende ich eine Klasse. Diese Klasse erweitert den <a title="Strings bequem formatieren" href="http://yoda.neun12.de/artikel-63">Formatter den ich bereits vorgestellt habe</a> und erbt dadurch die Methoden <code>printf()</code> und <code>sprintf()</code>. Zu der Verarbeitung der Key-Value-Paare in der Foreach-Schleife komme ich später noch.</p>
<p>Wenn wir jetzt aber anstatt der Ordered List eine Unordered List erstellen wollen, schreiben wir uns eine neue Klasse!? Das wäre ja nicht Sinn der Aktion, wir wollen ja eine flexible Lösung haben.<br />
Es fehlt die Möglichkeit einen Listen-Typ auszuwählen. Was dafür geändert werden muss, sind die Template-Strings mit dem HTML-Code. Die einfachste Methode um die Templates für verschiedene Listen-Typen zu speichern ist ein Array. Wir benötigen also ein Array das zu jeden Listen-Typ (ol, ul und div) zwei Template-Strings speichert (den inneren Teil und den äußeren Teil).<br />
<div class="gistem"><div id="gist-2138508" 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_once</span> <span class="s1">&#39;class-formatter.php&#39;</span><span class="p">;</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="k">class</span> <span class="nc">Lister</span> <span class="k">extends</span> <span class="nx">Formatter</span></div><div class='line' id='LC5'><span class="p">{</span>	</div><div class='line' id='LC6'>	<span class="k">public</span> <span class="nv">$templates</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(){</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">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></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>	<span class="p">}</span></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">(){</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC17'>			<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='LC18'>						<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='LC19'>						<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='LC20'>			<span class="p">),</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>			<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='LC23'>						<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='LC24'>						<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='LC25'>			<span class="p">),</span></div><div class='line' id='LC26'><br/></div><div class='line' id='LC27'>			<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='LC28'>						<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='LC29'>						<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='LC30'>			<span class="p">),</span></div><div class='line' id='LC31'>		<span class="p">);</span></div><div class='line' id='LC32'><br/></div><div class='line' id='LC33'>	<span class="p">}</span></div><div class='line' id='LC34'><br/></div><div class='line' id='LC35'>	<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='LC36'><br/></div><div class='line' id='LC37'>		<span class="c1">// get templates if not already set</span></div><div class='line' id='LC38'>		<span class="k">if</span><span class="p">(</span> <span class="k">empty</span><span class="p">(</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC39'>			<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">();</span></div><div class='line' id='LC40'><br/></div><div class='line' id='LC41'>		<span class="c1">// check if the requested list-typ is defined</span></div><div class='line' id='LC42'>		<span class="k">if</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">$this</span><span class="o">-&gt;</span><span class="na">templates</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC43'>			<span class="k">return</span> <span class="k">FALSE</span><span class="p">;</span></div><div class='line' id='LC44'><br/></div><div class='line' id='LC45'>		<span class="c1">// create list</span></div><div class='line' id='LC46'>		<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='LC47'>		<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='LC48'><br/></div><div class='line' id='LC49'>		<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='LC50'><br/></div><div class='line' id='LC51'>			<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='LC52'>			<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='LC53'><br/></div><div class='line' id='LC54'>			<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">$this</span><span class="o">-&gt;</span><span class="na">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='LC55'>		<span class="p">}</span></div><div class='line' id='LC56'><br/></div><div class='line' id='LC57'>		<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">$this</span><span class="o">-&gt;</span><span class="na">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='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="p">}</span></div><div class='line' id='LC62'><br/></div><div class='line' id='LC63'><span class="nv">$data</span> <span class="o">=</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></div><div class='line' id='LC64'><br/></div><div class='line' id='LC65'><span class="nv">$list</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Lister</span><span class="p">();</span></div><div class='line' id='LC66'><span class="k">echo</span> <span class="nv">$list</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="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC67'><span class="k">echo</span> <span class="nv">$list</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="s1">&#39;ul&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC68'><span class="k">echo</span> <span class="nv">$list</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="s1">&#39;div&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC69'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2138508/5ae8550b2206eb6d8880e8b31d88e6be8a712351/file1.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2138508#file_file1.php" style="float:right;margin-right:10px;color:#666">file1.php</a>
            <a href="https://gist.github.com/2138508">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>Wunderbar. Jetzt können wir ganz einfach durch Angabe eines Listen-Types und eines Arrays eine Liste erzeugen. Das spart uns schon mal viel Arbeit, denn anstatt für jede Liste eine eigene Foreach-Schleife zu schreiben, benutzen wir einfach die Instanz unserer Klasse.<br />
Und wenn wir jetzt der Liste noch eine CSS-Klasse mitgeben wollen, dann schreiben wir uns eine andere Klasse die das kann!? Vielleicht erweitern wir einfach unsere Klasse dahingehend das sie nicht so starr ist.</p>
<h3>Noch mehr Flexibilität</h3>
<p>Die fest eingebauten Templates sind zu starr für unsere Zwecke, irgendwie müssen die raus aus der Klasse. Zu Anfang habe ich ja schon geschrieben das die Templates in die eine Datei, die Klasse die aus den Templates fertiges HTML macht in eine andere Datei soll. Das die Klasse oben einen Konstruktor hat, hat seinen Sinn. Denn das ist unser nächster Ansatzpunkt.<br />
Wir schieben einfach die Methode get_templates() in eine Template-Klasse und übergeben der Lister-Klasse ein Objekt das aus der Template-Klasse erzeugt wurde. Damit können wir innerhalb der Lister-Klasse auf die Templates zugreifen. Alles klar soweit? Nicht? Einfach das Beispiel anschauen.<div class="gistem"><div id="gist-2145820" 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_once</span> <span class="s1">&#39;class-formatter.php&#39;</span><span class="p">;</span></div><div class='line' id='LC3'><span class="k">require_once</span> <span class="s1">&#39;class-templates.php&#39;</span><span class="p">;</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'><span class="k">interface</span> <span class="nx">Templates</span></div><div class='line' id='LC6'><span class="p">{</span></div><div class='line' id='LC7'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">();</span></div><div class='line' id='LC8'><span class="p">}</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'><span class="k">class</span> <span class="nc">Lister</span> <span class="k">extends</span> <span class="nx">Formatter</span></div><div class='line' id='LC11'><span class="p">{</span>	</div><div class='line' id='LC12'>	<span class="k">public</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='LC13'><br/></div><div class='line' id='LC14'>	<span class="k">public</span> <span class="nv">$templates</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span> <span class="nx">Templates</span> <span class="nv">$templates</span> <span class="p">){</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>		<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='LC19'><br/></div><div class='line' id='LC20'>		<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">();</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>	<span class="p">}</span></div><div class='line' id='LC23'>	<span class="k">protected</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">(){</span></div><div class='line' id='LC24'><br/></div><div class='line' id='LC25'>		<span class="k">if</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='LC26'>			<span class="k">throw</span> <span class="k">new</span> <span class="nx">Exception</span><span class="p">(</span> <span class="s1">&#39;No templates defined&#39;</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">templates</span> <span class="o">=</span> <span class="o">&amp;</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></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="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='LC33'><br/></div><div class='line' id='LC34'>		<span class="c1">// get templates if not already set</span></div><div class='line' id='LC35'>		<span class="k">if</span><span class="p">(</span> <span class="k">empty</span><span class="p">(</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">templates</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC36'>			<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">get_templates</span><span class="p">();</span></div><div class='line' id='LC37'><br/></div><div class='line' id='LC38'>		<span class="c1">// check if the requested list-typ is defined</span></div><div class='line' id='LC39'>		<span class="k">if</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">$this</span><span class="o">-&gt;</span><span class="na">templates</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span></div><div class='line' id='LC40'>			<span class="k">return</span> <span class="k">FALSE</span><span class="p">;</span></div><div class='line' id='LC41'><br/></div><div class='line' id='LC42'>		<span class="c1">// create list</span></div><div class='line' id='LC43'>		<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='LC44'>		<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='LC45'><br/></div><div class='line' id='LC46'>		<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='LC47'><br/></div><div class='line' id='LC48'>			<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='LC49'>			<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='LC50'><br/></div><div class='line' id='LC51'>			<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">$this</span><span class="o">-&gt;</span><span class="na">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='LC52'>		<span class="p">}</span></div><div class='line' id='LC53'><br/></div><div class='line' id='LC54'>		<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">$this</span><span class="o">-&gt;</span><span class="na">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='LC55'><br/></div><div class='line' id='LC56'>	<span class="p">}</span></div><div class='line' id='LC57'><br/></div><div class='line' id='LC58'><span class="p">}</span></div><div class='line' id='LC59'><br/></div><div class='line' id='LC60'><span class="nv">$data</span> <span class="o">=</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></div><div class='line' id='LC61'><br/></div><div class='line' id='LC62'><span class="nv">$list</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Lister</span><span class="p">(</span> <span class="k">new</span> <span class="nx">Simple_List_Templates</span> <span class="p">);</span></div><div class='line' id='LC63'><br/></div><div class='line' id='LC64'><span class="k">echo</span> <span class="nv">$list</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="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC65'><span class="k">echo</span> <span class="nv">$list</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="s1">&#39;ul&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC66'><span class="k">echo</span> <span class="nv">$list</span><span class="o">-&gt;</span><span class="na">get_list</span><span class="p">(</span> <span class="s1">&#39;div&#39;</span><span class="p">,</span> <span class="nv">$data</span> <span class="p">);</span></div><div class='line' id='LC67'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2145820/f588978c79e07218ae48d3ac244f42c56ace671f/class-lister.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2145820#file_class_lister.php" style="float:right;margin-right:10px;color:#666">class-lister.php</a>
            <a href="https://gist.github.com/2145820">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="cp">&lt;?php</span></div><div class='line' id='LC2'><span class="k">class</span> <span class="nc">Simple_List_Templates</span> <span class="k">implements</span> <span class="nx">Templates</span></div><div class='line' id='LC3'><span class="p">{</span></div><div class='line' id='LC4'>	<span class="k">public</span> <span class="k">function</span> <span class="nf">get_templates</span><span class="p">(){</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>		<span class="k">return</span> <span class="k">array</span><span class="p">(</span></div><div class='line' id='LC7'>			<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='LC8'>						<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='LC9'>						<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='LC10'>			<span class="p">),</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>			<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='LC13'>						<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='LC14'>						<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='LC15'>			<span class="p">),</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>			<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='LC18'>						<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='LC19'>						<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='LC20'>			<span class="p">),</span></div><div class='line' id='LC21'>		<span class="p">);</span></div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'>	<span class="p">}</span></div><div class='line' id='LC24'><span class="p">}</span></div><div class='line' id='LC25'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2145820/971a998cc54775c4ebf62ab7954b58098af2690b/class-templates.php" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2145820#file_class_templates.php" style="float:right;margin-right:10px;color:#666">class-templates.php</a>
            <a href="https://gist.github.com/2145820">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>Ich möchte zur Erklärung schnell mal über die Klasse Lister fliegen. Am Anfang sehen wir ein Interface, das hat in sofern seinen Sinn, als dass man damit bestimmte Bedingungen festlegen kann. So wird im Konstruktor der Lister-Klasse festgelegt das ein Objekt übergeben werden muss das aus einer Klasse erstellt wurde die das Interface <code>Template</code> imlementiert. Damit gehen wir sicher, das dass übergebene Objekt über die Methode <code>get_templates()</code> verfügt. Würden wir versuchen ein Objekt zu übergeben das nicht über diese Methode verfügt, sprich nicht das Interface Template implementiert, würden wir beim Aufruf der Methode <code>get_templates()</code> eine Fehlermeldung bekommen. Damit gehen wir zwar nicht sicher das die Template-Klasse uns beim Aufruf der Methode <code>get_templates()</code> auch ein Array mit Templates zurück gibt, es hilft aber unter Umständen sehr bei der Fehlersuche wenn mal wieder etwas nicht funktioniert.<br />
Im  Konstruktor sehen wir das bei der Parameterübergabe darauf gepocht wird das dass übergeben Objekt das Interface Templates implementieren muss. Das übergebene Objekt speichern wir uns in einer Klassen-Variablen damit wir in anderen Methoden ggf. darauf zugreifen können. Zum Schluss holen wir uns noch die heiß begehrten Templates von der Template-Klasse.<br />
Als nächstes sehen wir dann eine get_templates() Methode. Die Methode sollte ja eigentlich ausgelagert werden. Hier steht sie quasi als Wrapper damit man z.B. noch weitere Kontrollen oder ähnliches einbauen kann. Natürlich hätte man auch z.B. in der Methode get_list() direkt auf die gespeicherte Instanz der Template-Klasse zugreifen können. Aber wir wollen uns ja mal angewöhnen gleich von Anfang an vernünftigen Code zu schreiben. Auch in Beispielen.<br />
Über get_list() muss ich hoffentlich keine Worte mehr verlieren. Templates abholen, Variablen ggf. verifizieren und desinfizieren, Liste basteln und ausgeben.</p>
<h3>Ein konkretes Beispiel</h3>
<p>Im Prinzip sind wir schon fertig und haben alle unsere gesteckten Ziele erreicht. Die Templates (Ausgabe) stehen in der einen Datei, die Verarbeitung der Daten (Logik) in einer anderen. Beides ist sauber voneinander getrennt. Kommen wir noch mal kurz darauf zurück warum das so sinnvoll sein kann.<br />
Nehmen wir einfach mal den Fall das wir relativ viele Templates haben in denen eine Footer definiert ist . Der könnte nach traditionellem HTML in etwa so aussehen:</p>
<pre class="brush:xml">&lt;div class="footer"&gt;
	&lt;p&gt;Copyright © 2001-2012&lt;/p&gt;
&lt;/div&gt;</pre>
<p>Nun kommt unser Kunde der gelesen hat das HTML5 der letzte Schrei ist und er möchte nun seine Webseite auf HTML5 umgestellt haben weil er befürchtet das sie ansonsten morgen nicht mehr erreichbar ist. Dazu müssten auch alle Footer-Bereiche nach <a title="W3Schools HTML5 Footer" href="http://www.w3schools.com/html5/tag_footer.asp">HTML5-Footer</a> konvertiert werden. Aus den Code oben müsste also in etwa folgendes werden:</p>
<pre class="brush:xml">&lt;footer&gt;
	&lt;p&gt;Copyright © 2001-2012&lt;/p&gt;
&lt;/footer&gt;</pre>
<p>Und das dann nicht nur auf einer Seite, sondern auf allen. Und nicht nur die Footer, sondern auch die Bereiche die eine <a title="W3Schools section" href="http://www.w3schools.com/html5/tag_section.asp">Section</a>, <a title="W3Schools nav" href="http://www.w3schools.com/html5/tag_nav.asp">Navigation</a>, <a title="W3Schools Article" href="http://www.w3schools.com/html5/tag_article.asp">Artikel</a>, usw. darstellen. Der richtige Zeitpunkt um eine Reihe böser Flüche los zu werden, denn das bedeutet tagelanges Wühlen in unübersichtlichen Codezeilen.<br />
Nun werfen wir einen Blick auf unsere Template-Klasse. Dort habe ich eine &#8220;Div-Liste&#8221; definiert mit der man z.B. solche Footer-Bereiche erzeugen kann. Anstatt nun den kompletten Code neu zu schreiben, tauscht man einfach seine Template-Klasse aus. Aus <code>Simple_List_Templates</code> wird dann z.B. <code>HTML5_List_Templates</code>. Das macht die Sache doch erheblich einfacher als in jeder HTML-Datei Änderungen manuell durchzuführen.<br />
Mit etwas Überlegung kommt man dann auch recht schnell darauf das es eine relativ blöde Idee ist die Template-Klasse beim erzeugen der Lister-Klasse fest zu verdrahten. Zu den Problem komme ich in einen anderen Artikel, dort will ich noch mal auf ein paar Feinheiten eingehen. Dann gehe ich auch noch mal auf die Key-Value-Paare in der Foreach-Schleife ein.</p>
<p>Aber wir sehen schon so, dass man mit der Trennung von Ausgabe und Logik für die Zukunft programmiert. Man kann sowohl die Logik als auch die Templates in verschiedenen Projekten erneut verwenden. Zudem sind Änderungen nicht mehr der Weltuntergang weil man unzählige Dateien durchsuchen und kontrollieren muss. Ich hoffe dem einen oder anderen einen kleinen Denkanstoß gegeben zu haben und wünsche viel Spaß beim selber Ausprobieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-65/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Performance ist nicht gleich Performance</title>
		<link>http://yoda.neun12.de/artikel-54</link>
		<comments>http://yoda.neun12.de/artikel-54#comments</comments>
		<pubDate>Fri, 06 Jan 2012 18:10:44 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=54</guid>
		<description><![CDATA[Ich bin kein großer Fan von Performance-Tests, dass sage ich gleich vorweg. Der Grund ist schlichtweg der, dass man vergleichbare Umgebungen voraussetzen muss um die Ergebnisse übertragbar zu machen. Wenn ich Plugin auf System A teste, rennt es noch wie doof. Teste ich es aber auf System B, hinkt und hakt es wie ein lahmer [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Ich bin kein großer Fan von Performance-Tests, dass sage ich gleich vorweg. Der Grund ist schlichtweg der, dass man vergleichbare Umgebungen voraussetzen muss um die Ergebnisse übertragbar zu machen. Wenn ich Plugin auf System A teste, rennt es noch wie doof. Teste ich es aber auf System B, hinkt und hakt es wie ein lahmer Gaul.<br />
Der Grund dafür liegt in der Optimierung der Systeme, sofern sie optimiert sind. Als Beispiel möchte ich nur die Anbindung an den MySQL-Server nennen. Hier gibt es etliche verschiedene Konfigurationen für die unterschiedlichsten Anwendungsfälle. Werden viele kleine Datensätze abgefragt, optimiert man den MySQL-Server anders als wenn man ihn für wenige große Datensätze benötigt. Somit würde das gleiche Plugin auf beiden Systemen unterschiedlich performant sein, alleine deshalb weil die Anbindung an die Datenbank anders ist.<br />
Nicht viel anders sieht es beim Server selber aus. Auch hier gibt es unzählige Optimierungsvarianten. Hinzu kommt noch unterschiedliche Server-Software, Betriebssysteme auf denen der Server läuft bis hin zu der Hardware die einen sehr wesentlichen Teil der Performance ausmacht. Wie soll man da einen gemeinsamen Nenner finden den man als Grundlage des Vergleichs heranziehen kann?</p>
<p>Viele Tests messen einfach das, was man einfach messen kann. Das wäre dann z.B. der Speicherverbrauch. Dann wird auch gerne die Ausführungszeit gemessen, was aber aufgrund der unterschiedlichen Systeme schon kaum noch Aussagekraft hat. Reicht das alles nicht, kommen gerne noch so obskure Faktoren wie Codezeilen oder Anzahl der verwendeten Hooks und Filter hinzu.<br />
Selten bis gar nicht wird die Code-Qualität bewertet. Dies ist für mich aber ein sehr wichtiges Kriterium. Denn ein schlampig programmiertes Plugin läuft oft auch schlampig, wirft Fehlermeldungen und ist nicht zukunftssicher. Häufig sieht man Plugins die über die Jahre gewachsen sind bis der Autor selber den Überblick verliert. Entweder wird das Plugin dann von Grund auf neu programmiert und weiter gepflegt. Oder aber, was häufiger vorkommt, man lässt das Plugin so wie es ist und kümmert sich nicht mehr darum.<br />
Nun ist Code-Qualität nicht ganz so einfach zu &#8220;messen&#8221;. Man muss den Code bewerten und hierzu eine Reihe von Kriterien heranziehen. das kostet Zeit und Mühe, weshalb es wohl niemand macht. Ein Kriterium wären zum Beispiel die Übersichtlichkeit. Das kann eigentlich jeder bewerten der sich den Code anschaut. Oder aber auch ob der Code ein Wust aus wilden Zeilen darstellt die kaum entzifferbar sind. Ist der Code gut kommentiert damit auch jemand der nicht so viel Ahnung von Programmierung hat versteht was da vor sich geht. Hält der Code sich an gewisse Programmierrichtlinien. Nur um mal einige wenige Kriterien zu nenne die man verwenden kann.<br />
All das kann man mit Noten bewerten, sogar relativ objektiv. Nun werden einige sagen &#8220;<em>Hauptsache es rennt!</em>&#8220;. Das ist natürlich auch ein Argument. Also zieht man noch die Anzahl an Fehlern, Notices usw. heran die das Plugin wirft wenn man es einsetzt. Auch grausam geschriebener Code kann fehlerfrei laufen. So kommt man schon zu einen recht guten und nachvollziehbaren Ergebnis.<br />
Kennt man sich bei der Programmierung ein wenig besser aus, dann kann man auch noch einzelne Code-Abschnitte genauer unter die Lupe nehmen. Ist der Code gemäß den empfohlenen Vorgaben geschrieben? Wenn nicht, macht es Sinn von den Vorgaben abzuweichen? Gibt es &#8220;merkwürdige&#8221; Programmiertechniken im Code oder ist alles auf den ersten Blick verständlich?</p>
<p>Für mich sind dies deutlich wichtigere Faktoren als Millisekunden Laufzeit und Megabyte Speicherverbrauch. Denn dies sind Faktoren die ich erklären kann, so dass auch jemand der weniger Ahnung hat versteht wie ich zu meinen Testergebnis gekommen bin. Und vor allem wird schnell deutlich wo noch Potenzial für Verbesserungen besteht. Code der leicht verständlich ist, wird auch gerne von anderen gepflegt, so dass er sich recht schnell verbreitet und mit der Zeit (hoffentlich) besser wird.</p>
<p>Ein ziemlich merkwürdiger Test macht hingegen gerade auf <a title="G+" href="https://plus.google.com/110569673423509816572/posts/bdbhLMhuaBU">Google+</a> die Runde. Es geht um den <a title="Dev4Press" href="http://www.dev4press.com/2012/blog/benchmark/plugins-performance-testing-2012-january/">Plugin-Test von MillaN</a>. <a title="Sergej Müller" href="http://ebiene.de/">Sergej Müller</a> hat sich wohl zurecht gefragt wie MillaN auf seine Ergebnisse kommt. Getestet wurden 35 Plugins aus den unterschiedlichsten Kategorien und mit einem Wert von 1 (schlecht) bis 5 (sehr gut) bewertet. Nur wie dieser Wert zustande kommt, bleibt vielen vollkommen unergründlich.<br />
Vielleicht ist weniger Speicherverbrauch besser als mehr? Also <a title="FPW Post Instructions" href="http://wordpress.org/extend/plugins/fpw-post-instructions/">FPW Post Instructions</a> bekommt eine 4 bei einem konstanten Speicherverbrauch von 0,1MB. GD Press Tools Pro verbraucht zwar 29 mal so viel Speicher (2,9MB), bekommt aber die Bestnote 5. Es scheint also etwas mit dem Speicherverbrauch im Frontend bzw. Backend zu tun zu haben. Denn GD Press Tools Pro verbraucht im Frontend &#8220;nur&#8221; 2MB Speicher, während FPW Post Instructions sowohl vorne wie hinten den gleichen Speicherverbrauch hat. So wirklich schlüssig ist das Konzept jedoch nicht. Denn <a title="WP Event Manager" href="http://wp-events-plugin.com/">Event Manager</a> bekommt die schlechteste Note 1 bei einem Speicherverbrauch von 5 bzw 3,7MB (Front-/Backend). GD Custom Posts And Taxonomies Tools Lite bekommt für die gleiche Leistung (1,5/1,3MB) eine noch ganz gute 4 verpasst. Event Manager ist also schlechter obwohl es mehr Speicher einspart?<br />
Ich könnte jetzt noch weiter rätseln ob es vielleicht an der Ausführungszeit liegt oder an der Anzahl der verwendeten Hooks. Vielleicht auch am Mondstand beim Test des Plugins. Man weiß es nicht und der Autor trägt auch nicht zur Aufklärung bei.</p>
<blockquote><p>Grade 2 plugins have only some elements of optimization, but they are close to grade 1. Adminimize plugin shouldn’t even load on the front end except for some elements, and that needs to be optimized.</p></blockquote>
<p>Das soll also der Grund sein warum einige Plugins schlechter sind als andere? Adminimize soll z.B. im Frontend nur das laden was es benötigt, was es im Prinzip auch macht, dies sollte aber optimiert werden? Hö? Es scheint also darum zu gehen was ein Plugin im Front- bzw. Backend lädt. Da ist es wohl egal welche Aufgabe es hat, wenn es nicht den geheimen Richtlinien des Autors entspricht, ist es schlecht.</p>
<p>Na dann schauen wir uns doch mal eins dieser supertollen Grade-5 Plugins an. Ich habe mich für GD Unit Converter entschieden und es mal unter die Lupe genommen. Was mir beim ersten Blick auffiel, waren folgende Codezeilen:</p>
<pre class="brush:php">$this-&gt;script = $_SERVER["PHP_SELF"];
$this-&gt;script = end(explode("/", $this-&gt;script));</pre>
<p>Und? Sinn erkannt? Also ich habe erst mal längere Zeit gerätselt was da passiert bis ich den Code kopiert und getestet habe. OK, ich schreibe es mal ein wenig um:</p>
<pre class="brush:php">$this-&gt;script = basename(__FILE__);</pre>
<p>So einfach kann PHP sein. Der nächste Horror folgt sofort:</p>
<pre class="brush:php">    private function plugin_path_url() {
        $this-&gt;plugin_url = plugins_url("/gd-unit-converter/");
        $this-&gt;plugin_path = dirname(dirname(__FILE__))."/";

        define("GDUNITCONVERTER_URL", $this-&gt;plugin_url);
        define("GDUNITCONVERTER_PATH", $this-&gt;plugin_path);
    }</pre>
<p>Da werden also zwei globale Variablen definiert die zwanzig Zeilen später bei einem <code>wp_enqueue_script()</code> wieder verwendet werden. Klar, man hätte auch einfach auf <code>$this-&gt;plugin_url</code> zurück greifen können (OOP, u know!?). Aber warum sparsam mit dem Speicher umgehen wenn man doch so schön sinnlos welchen durch Verwendung globaler Variablen verschwenden kann?<br />
Ach ja. Weil man anderen gerne vorschreibt sie sollen doch bitte schön sparsam mit dem Speicher umgehen:</p>
<blockquote><p>I am sure that many developers will say that these results are not important, but considering that most of the WordPress users are on shared hosting with limited memory and resources available to them, this is most important thing to have plugins they need and still have server running fine.</p></blockquote>
<p>Also Wasser predigen und Wein saufen. Contactform 7 bekommt übrigens nur eine schwache 2, dafür aber eine extra Erwähnung:</p>
<blockquote><p>All tested plugins, but one, used the internal WordPress AJAX handling. Contact Form 7 uses own handler and that is not something I can recommend. Using WP handler is best solution considering that it is already written with security concerns in mind and it is very easy to use, making plugin fit better with WP development concepts.</p></blockquote>
<p>Muss ich extra erwähnen das GD Unit Converter seinen eigenen Caching-Mechanismus mitbringt und mal ganz elegant auf den in WordPress bereits eingebauten Caching-Mechanismus pfeift?<br />
Natürlich darf auch der eigene Logger nicht fehlen, der zwar nicht genutzt wird aber dennoch geladen werden muss (require_once). Über solch lustige Konstrukte wie folgenden wundert man sich dann schon gar nicht mehr.</p>
<pre class="brush:php">$js_url = defined("SCRIPT_DEBUG") &amp;&amp; SCRIPT_DEBUG ? "js/src/unit-converter.js" : "js/unit-converter.js";</pre>
<p>Wenn <code>SCRIPT_DEBUG</code> definiert ist, sollen die JavaScripte aus dem nicht vorhandenen Verzeichnis <code>js/src/</code> geladen werden. Das gibt lustige Fehlermeldungen wenn man sein Blog im Debug-Modus startet. So lustig, das man das Plugin sofort deaktiviert.</p>
<p>Es ist einfach schlau daher zu reden und dann selber solch einen Mist zu veröffentlichen. Gefühlt die Hälfte des Codes die das Plugin umfasst ist sinnlos, wird nicht benötigt, verstößt gegen Coding Guidelines oder ist schlichtweg Speicherverschwendung. Hier sollte der Herr MilanN vielleicht selber noch mal Hand anlegen und das Plugin um fluffige 90% Code reduzieren bevor er ihm die Bestnote 5 verpasst. Ich habe mich vorhin mal eine Stunde lang hingesetzt und versucht das Plugin von Scratch (also ohne Vorlage) nachzubauen. Nach einer Stunde hatte ich bereits brauchbare Ergebnisse und kam mit einem Zehntel an Code aus. Der Speicherverbrauch dürfte noch sparsamer sein, da ich nicht total unnützes Zeug lade, sondern mich darauf beschränke das zu nutzen, was ich benötige.</p>
<p>Solche Tests die nicht klar machen wie eine Bewertung zu Stande kommt und bei denen der Autor sich die Testbedingungen so zurecht biegt das er sich selbst Bestnoten verpassen kann, kann man getrost außer Acht lassen. Hier muss sich jeder selber fragen wie objektiv der Test sein kann wenn die eigenen Plugins den Maßstab vorgeben. Da sollte man sich als Autor nicht wundern wenn andere einen auf den Zahn fühlen und zeigen wo der Mops die Locken hat.</p>
<h3>Fazit</h3>
<p><em>Gute Tests sind objektiv und vergleichen nicht eigene Arbeit mit der von Fremden.</em> Wird die eigene Arbeit dennoch im Test mit einbezogen, dürfen die Testkriterien nicht auf die eigene Optimierung hin zugeschnitten sein.<br />
<em>Die Testkriterien sollten klar und verständlich sein.</em> Was man als Leser nicht nachvollziehen kann, ist nichts wert. Ist es fraglich wie eine Bewertung zu Stande gekommen ist, ist der ganze Test wertlos. <a title="Texto" href="http://www.texto.de/plugin-performance-ich-kapiers-nicht-1323/">Moinka Thon-Soun hat auf Texto</a> in einen Artikel beschrieben wie ein schlecht gemachter Test auf einen Laien wirkt. Nämlich verwirrend, sonst nichts.<br />
<em>Die Tests sollten nachprüfbar sein.</em> Dazu müssen Testscripte zugänglich sein und die Testumgebung erläutert werden.<br />
<em>Test sollten nicht auf einen einzelnen Aspekt fokussiert sein.</em> Jeder Test hat einen Schwerpunkt, dies rechtfertigt jedoch nicht eine schlechte Bewertung weil ein Testkandidat eben nicht genau den Schwerpunkt trifft. Ein guter Tests umfasst mehrere Aspekte und bildet aus den Einzelergebnissen eine Bewertung. Jedes Plugin hat seine Stärken und Schwächen. Ein Plugin das optisch gut gestylt daher kommt, verbraucht eben mehr Speicher (mehr Grafiken, mehr CSS, mehr Code) als ein Plugin welches sehr spartanisch gestaltet ist. Was nützt einem aber ein Plugin das am falschen Ende spart, wenn es durch seinen Purismus nur schwer bedienbar ist?<br />
<em>Tests sollten sich auf das beschränken, was unter anderen Systemen ähnliche (vergleichbare) Ergebnisse liefert.</em> Eine systemunabhängiger Wert sind z.B. Fehlermeldungen (auch Notices und Deprecated Meldungen) . Die werden auf jeden System gleich sein, denn falsch ist hier wie da falsch.<br />
<em>Test sollten realitätsnah sein.</em> Im Labor gelten andere Regeln als in der Wildnis. Selbst wenn ein Plugin im Test wunderbar abschneidet, kann es dadurch versagen das es andere Plugins behindert oder gar komplett ausschaltet. Man kann nicht alle Kombinationen von Plugins prüfen, jedoch gibt es eine Reihe von Regeln für ein friedliches Miteinander die man beachten sollte.</p>
<p>Also bitte nicht jeden Test blauäugig Glauben schenken. Lieber einmal mehr als einmal zu wenig an den Ergebnissen zweifeln. Wir wissen doch alle: Die Statistik die man selber gefälscht hat, ist immer noch die beste <img src="http://yoda.neun12.de/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" /> </p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-54/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>XML mit poEdit übersetzen</title>
		<link>http://yoda.neun12.de/artikel-42</link>
		<comments>http://yoda.neun12.de/artikel-42#comments</comments>
		<pubDate>Sat, 10 Dec 2011 18:57:26 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=42</guid>
		<description><![CDATA[Vor einiger Zeit stand ich vor dem Problem das ich XML-Dateien hatte die zu übersetzende Zeichenketten enthielten. Mein erster, wahrscheinlich auch naheliegendster, Gedanke war, die zu übersetzenden Zeichenketten einfach in ein __() zu packen und zu hoffen das poEdit dies erkennt. Das Problem daran war aber, dass __() eine PHP-Funktion ist und die XML-Dateien mit [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Vor einiger Zeit stand ich vor dem Problem das ich XML-Dateien hatte die zu übersetzende Zeichenketten enthielten. Mein erster, wahrscheinlich auch naheliegendster, Gedanke war, die zu übersetzenden Zeichenketten einfach in ein <code>__()</code> zu packen und zu hoffen das poEdit dies erkennt. Das Problem daran war aber, dass <code>__()</code> eine PHP-Funktion ist und die XML-Dateien mit <code>simplexml</code> verarbeitet wurden. Die PHP-Funktion würde also erst gar nicht ausgeführt werden, selbst wenn poEdit die Zeichenketten erkennen würde. Unter Zuhilfenahme von Suchmaschinen lies sich aber recht schnell eine Lösung für das Problem finden.</p>
<p><a class="highslide" onclick="return hs.expand(this)"  href="../wp-content/uploads/2011/12/poedit_1.jpg"><img class="size-thumbnail wp-image-44 alignright" title="poEdit Einstellungen" src="../wp-content/uploads/2011/12/poedit_1-150x150.jpg" alt="" width="150" height="150" /></a>In poEdit muss man einen neuen Parser anlegen (Datei -&gt; Einstellungen -&gt; Parser):</p>
<ul>
<li>Sprache: <code>XML</code></li>
<li>Durch Semikola getrennte Liste der Dateiendungen (z.B. <em>.cpp;</em>.h): <code>*.xml</code></li>
<li>Parser Befehl: <code>xgettext --force-po -o %o %C %K %F -L glade</code></li>
<li>Ein Eintrag in der Schlüsselwortliste: <code>-k%k</code></li>
<li>Ein Eintrag in der Eingabedatei-Liste: <code>%f</code></li>
<li>Zeichensatz des Quellcodes: <code>--from-code=%c</code></li>
</ul>
<p>Erzeugt man nun einen neuen Katalog, so fügt man in den Katalogoptionen unter Schlüsselwörter noch ein zusätzliches Schlüsselwort ein. Diesen kann man relativ frei wählen, ich habe mich hier für <code>translate</code> entschieden.<br />
In der XML-Datei muss man nun noch die zu übersetzenden Zeichenketten mit seinen gewählten Schlüsselwort umschließen:</p>
<pre class="brush:xml">&lt;root&gt;
  &lt;title&gt;&lt;translate&gt;My headline&lt;/translate&gt;&lt;/title&gt;
  &lt;text&gt;&lt;translate&gt;This is the text&lt;/translate&gt;&lt;/text&gt;
  &lt;options&gt;
   &lt;font&gt;strong&lt;/font&gt;
   &lt;color&gt;black&lt;/color&gt;
  &lt;/options&gt;
&lt;/root&gt;</pre>
<p>poEdit ist nun in der Lage die in der XML-Datei mit <code>translate</code> gekennzeichneten Zeichenketten zu übersetzen.</p>
<p><a class="highslide" onclick="return hs.expand(this)"  href="http://yoda.neun12.de/wp-content/uploads/2011/12/poedit_2.jpg"><img class="alignleft size-thumbnail wp-image-43" title="poEdit Einstellungen" src="http://yoda.neun12.de/wp-content/uploads/2011/12/poedit_2-150x150.jpg" alt="" width="150" height="150" /></a>Unter Windows kann es vorkommen das man noch eine Fehlermeldung erhält das <code>glade</code> und/oder <code>expat</code> nicht unterstützt werden. Das liegt an der mit poEdit ausgelieferten Version von <code>gettext</code>. In diesen Fall sollte man sich das vom <a title="gnuwin32 Projekt" href="http://gnuwin32.sourceforge.net/packages/gettext.htm">gnuwin32 Projekt bereit gestellte gettext-Paket</a> runterladen.<br />
Die in dem Paket enthaltenen Dateien kopiert man sich dann in <code>poedit/bin/</code> (vorher das Verzeichnis sichern). In einigen Quellen wird angegeben das man lediglich die <code>gettext.exe</code> kopieren muss, andere sagen man kopiert sich die <code>gettext.exe</code>, startet sie und schaut welche Abhängigkeiten/Bibliotheken (.dll Dateien) noch fehlen. Ich habe einfach das komplette Paket kopiert (WinXP / Win7) und es funktioniert einwandfrei.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-42/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Zugriffsbeschränkungen</title>
		<link>http://yoda.neun12.de/artikel-30</link>
		<comments>http://yoda.neun12.de/artikel-30#comments</comments>
		<pubDate>Sat, 12 Mar 2011 10:37:52 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Themes]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=30</guid>
		<description><![CDATA[Wer Plugins oder Themes selber schreibt wird sich früher oder später auch Gedanken über die Dateizugriffe machen müssen. Nicht jeder soll von Außen Zugriff auf unsere Dateien haben, wir möchten doch ganz gerne steuern wer welche Datei aufrufen darf. Meistens ist ein Aufruf a lá http://www.example.com/wp-content/plugins/a-plugin/conection_data.php eher harmlos, da versucht wird das Script auszuführen anstatt [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Wer Plugins oder Themes selber schreibt wird sich früher oder später auch Gedanken über die Dateizugriffe machen müssen. Nicht jeder soll von Außen Zugriff auf unsere Dateien haben, wir möchten doch ganz gerne steuern wer welche Datei aufrufen darf.</p>
<p>Meistens ist ein Aufruf a lá <code>http://www.example.com/wp-content/plugins/a-plugin/conection_data.php</code> eher harmlos, da versucht wird das Script auszuführen anstatt es anzuzeigen. Nicht zuletzt bleibt aber jedoch die Möglichkeit via <code>include(_once)</code> und <code>require(_once)</code> das Script auch von entfernten Servern aus einzubinden und auszuführen.<br />
Wie dem auch sei, ein mulmiges Gefühl bleibt immer zurück wenn jeder auf alle möglichen Dateien einfach per Browser oder PHP-Funktion zugreifen kann. Daher ist es bereits jetzt Best Practice seine Scripte und Dateien mit einer Kontrolle vor solch ungewollten Zugriffe zu schützen.</p>
<p>In vielen Plugins sieht man solchen und vergleichbaren Code</p>
<pre class="brush:php">&lt;?php 
  if ( ! defined( 'My_PLUGIN_VAR' ) ) 
    die( 'No direct script access allowed' );
?&gt;</pre>
<p>In der Plugin-Datei wird eine Konstante definiert (<code>MY_PLUGIN_VAR</code>) und in allen anderen Dateien wird abgefragt ob diese Konstante definiert ist. Ist sie es nicht, wurde womöglich direkt auf diese Datei zugegriffen was unerwünscht ist. Es gibt hier verschiedene Techniken diese Kontrolle durchzuführen.<br />
Und die Plugin-Datei selber will ja auch noch davor geschützt sein direkt aufgerufen zu werden:</p>
<pre class="brush:php">&lt;?php 
  if ( ! function_exists( 'add_action' ) ) 
    die( 'No direct script access allowed' );
?&gt;</pre>
<p>Falls die Funktion <code>add_action</code> nicht definiert wurde, was darauf schließen lässt das WordPress nicht gestartet wurde, kann man von einem direkten Zugriff auf die Plugin-Datei ausgehen. Auch hier gibt es verschiedene Wege um zu prüfen ob WordPress bereits gestartet wurde oder nicht.</p>
<p>Das ist alles schön und gut, aber auch aufwendig und fehleranfällig. Stellen wir uns mal ein Plugin mit mehreren Unterverzeichnissen und in jedem Unterverzeichnis wiederum mehreren Dateien vor. Arbeitet man alleine an dem Plugin, ist es schon schwer den Überblick zu behalten ob man wirklich jede Datei geschützt hat. Bei einem Projekt an denen mehrere Personen mitarbeiten ist die Gefahr um so größer das einer der Mitarbeiter vergisst einen entsprechenden Schutz einzubauen. Und so wie Murphy es will, ist es ausgerechnet die Datei, mit den größten Schwachstellen die niemand kontrolliert hat.<br />
Hinzu kommt das man mit diesen Techniken unter Umständen nicht jede Datei schützen kann. In JavaScript-Dateien wird es schon schwer mit diesen Techniken einen unerwünschten Zugriff zu verhindern. Spätestens bei CSS-Dateien müsste man gänzlich aufgeben.</p>
<p>Besser wäre es wenn man zentral regeln könnte auf welche Dateien zugegriffen werden darf und welche nicht. Der Apache Webserver bietet uns solch eine Möglichkeit in Form von <code>.htaccess</code>. Dazu benötigen wir im Grunde genommen nur zwei Regeln. Eine die die erlaubten Zugriffe beinhaltet und eine die die unerlaubten Zugriffe beinhaltet.</p>
<pre class="brush:shell">&lt;FilesMatch "\.(php|xml)$"&gt;
    Order allow,deny
    Deny from all
&lt;/FilesMatch&gt;

&lt;FilesMatch "(\.txt|\.htm?l|index\.php)$"&gt;
  Order allow,deny
  Allow from all
&lt;/FilesMatch&gt;</pre>
<p>Im ersten Block verbieten wir sämtliche Zugriffe auf alle PHP- und XML-Dateien. Die Liste kann man noch um weitere Dateitypen erweitern, je nach Bedarf. Da wir aber nicht alle Zugriffe grundsätzlich sperren wollen, heisst ja <code>readme.txt</code> und nicht <code>readme_not.txt</code>, erlauben wir im zweiten Block den Zugriff auf bestimmte Dateitypen (<code>.txt</code> und <code>.htm[l]</code>), sowie auf die <code>index.php</code>. Auch hier kann man je nach Bedarf bestimmte Dateitypen und Dateien hinzufügen bzw. entfernen.<br />
Das Beste an der Sache ist aber, es wirkt sich auch auf alle Unterverzeichnisse aus, wir müssen uns also keine Gedanken darüber machen ob nicht zufällig die eine Datei, die die Schwachstelle im System darstellt, nicht geschützt ist.</p>
<p>Wo viel Licht ist, ist natürlich auch viel Schatten. Denn auf anderen Webservern als den Apache (z.B. lighttpd, IIS, usw.) funktioniert das natürlich nicht. Entweder man legt für jeden Webserver dann entsprechende Konfigurations-Dateien an, sofern möglich, prüft auf die Server-Software und verweigert den Dienst oder nutzt diese Technik nur zusätzlich um auf Nummer sicher zu gehen.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-30/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Umsonst arbeiten?</title>
		<link>http://yoda.neun12.de/artikel-25</link>
		<comments>http://yoda.neun12.de/artikel-25#comments</comments>
		<pubDate>Sun, 13 Feb 2011 16:43:00 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=25</guid>
		<description><![CDATA[In meinem letzten Artikel hatte ich mich ja ein wenig über einen Text von Daniela Warndorf ausgelassen. Dabei geht es um die Frage wie man mit dem Problem von unbezahlter Arbeit umgehen soll. Genau genommen geht es um kostenlose Probearbeiten, die angeblich hauptsächlich nur bei Textern vorkommen. Zitat &#8220;In kaum einer anderen Branche wird mit [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>In <a href="http://yoda.neun12.de/artikel-23">meinem letzten Artikel</a> hatte ich mich ja ein wenig über <a href="http://offensichtlich.wordpress.com/2011/02/08/von-steaks-autos-und-milch-oder-nein-hier-gibt-es-nichts-umsonst/">einen Text von Daniela Warndorf </a>ausgelassen. Dabei geht es um die Frage wie man mit dem Problem von unbezahlter Arbeit umgehen soll. Genau genommen geht es um kostenlose Probearbeiten, die angeblich hauptsächlich nur bei Textern vorkommen. Zitat &#8220;<em>In kaum einer anderen Branche wird mit einer solchen Selbstverständlichkeit nach kostenlosen Probearbeiten verlangt.</em>&#8221;</p>
<h3>Blick über den Tellerrand</h3>
<p>Für mich ist das keine provokante These mehr, sondern ignorieren der Realität. Schauen wir uns doch einfach mal ein paar Berufe an in denen es die reine Selbstverständlichkeit ist das man &#8220;kostenlos&#8221; arbeitet.<br />
Da hätten wir zum Beispiel die Architekten. Bei größeren Projekten werden gerne so genannte &#8220;Wettbewerbe&#8221; ausgeschrieben. Um an den Wettbewerb teilnehmen zu dürfen, müssen die Architekten einen kompletten Entwurf inkl. Modell abliefern. In seltenen Fällen bekommen die zweit- und drittplatzierten noch ein kleines Honorar für ihre Arbeit. Alle anderen hingegen gehen leer aus. Das sind einige hundert Arbeitsstunden die Architekten unbezahlt leisten um an einen Auftrag zu kommen. Und dies vollkommen selbstverständlich und ohne großes Murren.<br />
Bleiben wir in der Baubranche, denn es geht auch eine Nummer kleiner. In der Baubranche werden Aufträge normalerweise ausgeschrieben. Es gibt ein Leistungsverzeichnis das der Bewerber mit Preisen auszufüllen hat. Derjenige der die entsprechende Eignung hat und den günstigsten Preis abliefert bekommt den Zuschlag. Alle anderen gehen leer aus. Die Abgabe eines Angebotes wird in keinster Weise vergütet. Dabei ist so ein Leistungsverzeichnis je nach Größe des Projektes zwischen 10 und mehreren tausend Seiten stark. Selbst wenn es nur wenige Positionen umfasst, verbringt man als Unternehmer etliche Stunden damit Preise zu kalkulieren, Preise für Materialen anzufordern und Angebote zu schreiben. Im Laufe eines Jahres kommen so auch etliche hunderte Stunden zusammen die in keinster Weise bezahlt werden.<br />
Aber natürlich gibt es auch Arbeiten die für uns mittlerweile so selbstverständlich sind, dass wir sie schon gar nicht mehr als solche erkennen. Ich kenne keine Metzgerei in der halbe Schweine oder ganze Kühe auf dem Tresen landen und wo ich mir das Kotelette selber raus schneiden muss. Auch im Supermarkt stehen keine Kühe an denen ich mir die Milch selber zapfen muss. Metzger und Supermarkt müssen Waren einkaufen, sie lagern, transportieren, in Regale und Theken einräumen und wenn sie nicht verkauft werden, wieder ausräumen und entsorgen. Dabei ist der Arbeitsaufwand manchmal noch nicht einmal das große Problem, sondern der Wareneinsatz. Denn für nicht verkaufte Ware bekommen weder Metzger noch Supermarkt von irgendjemanden Geld. Das sind Verluste die der Unternehmer selber zu tragen hat. So etwas fällt unter den Begriff Unternehmerisches Risiko.</p>
<h3>Kostenlos ist nicht umsonst</h3>
<p>So gesehen sind digitale Unternehmer, wie z.B. Texter, wirklich gut dran. Denn sie investieren lediglich etwas Zeit und haben so gut wie keinen Wareneinsatz. Die Verluste sind also überschaubar. Verluste, sei es durch unverkäufliche Ware oder nicht bezahlte Arbeit, gehören zum Geschäftsalltag unabwendbar hinzu. Das ist nun einmal das Risiko das man als Unternehmer eingeht und das den selbstständigen Unternehmer vom Angestellten, der normalerweise seine komplette Zeit bezahlt bekommt, unterscheidet. Die Frage kann also nicht sein wie man unbezahlte Arbeit vermeidet, sondern wie man Risiken, die das Geschäftsleben mit sich bringt, kalkuliert und wie man mit ihnen umgeht.<br />
Probearbeiten und Referenzen sind Teil des Grundkapitals. Eine Tatsache die viele Jungunternehmer entweder gar nicht begreifen oder schlichtweg ignorieren. So wie man ein Startkapital in Form von Finanzen und eine Grundausstattung in Form von Hard- und Software, Büroausstattung, usw. benötigt, benötigt man ebenso Schaustücke die man dem Kunden präsentieren kann.<br />
Im besten Fall sind es Referenzen die man vorzeigen kann. Denn bei Referenzen handelt es sich in der Regel um Projekte für die man bereits Geld bekommen hat, die Schaustücke sind also bereits bezahlt. Allerdings stellt sich hier die Frage wie man an Referenzen kommen soll. Denn auch eine Referenz ist ja schließlich ein Auftrag der erst einmal erteilt und durchgeführt werden will.<br />
Wenn man also (noch) keine Referenzen hat, dann muss man entweder Zeit in Schaustücke investieren oder mit (kostenlosen) Probearbeiten den Kunden überzeugen.</p>
<h3>Investition in die Zukunft</h3>
<p>Für Programmierer wäre es z.B. eine Möglichkeit Open Source Software zu veröffentlichen. Texter hätten z.B. die Möglichkeit über einen Blog ihre Fähigkeiten und Talente zu präsentieren. Webdesigner können z.B. für gemeinnützige Vereine Webseiten erstellen, usw. Dies alles ist zwar erst einmal &#8220;unbezahlte&#8221; Arbeit, jedoch sollte man es als Investition betrachten die sich vielleicht erst viele Jahre später auszahlt. Denn im Grunde genommen geht es doch darum sich einen Namen zu machen und sich zu präsentieren. Da ist es wahrscheinlich sehr suboptimal wenn die Open Source Software &#8220;Pimpernelli &#8211; Das lustige Puffspiel&#8221; heißt, der Blog nur so vor bissigen und sarkastischen Beiträgen strotzt, die &#8220;gemeinnützige Organisation&#8221; für die man Webseiten erstellt sich als Webseite mit sehr vielen nackten Frauenkörpern heraus stellt.<br />
Hier bewahrheitet sich der Grundsatz privates und geschäftliches tunlichst voneinander zu trennen. Es gibt sicherlich den einen oder anderen bei dem der Mix aus Privat und Geschäftlich funktioniert weil er privat genauso professionell ist wie geschäftlich. Wer aber gerne mal seine Meinung sagen möchte, sollte beides trennen. Denn im Geschäftsleben hat die eigene Meinung nur sehr geringen Stellenwert.</p>
<p>Hat man erst einmal ordentlich Zeit in Referenzen und andere Arbeiten investiert, hat man sich also einen Namen gemacht und kann was vorzeigen, kann man langsam mal dazu übergehen etwas zu verlangen. Wenn dem Kunden die bisherigen Referenzen nicht ausreichen und er eine speziell auf sich zugeschnittene Probearbeit sehen will, so sollte man sich diese Arbeit auch bezahlen lassen. Dabei sollte man aber nicht zu gierig sein, auch hier geht man ein gewisses unternehmerisches Risiko ein. Zu hohe Preise verscheuchen die potentiellen Kunden, zu niedrige Preise erhöhen die Verluste.<br />
Ein Kostenvoranschlag für eine Reparatur kostet in einer Autowerkstatt oft so um die 30 Euro. Damit deckt die Werkstatt aber in den seltensten Fällen alle tatsächlich angefallenen Kosten. Diese liegen meistens um das zwei- bis dreifache darüber. Den Fehlbetrag holen sich die Werkstätten jedoch bei einem späteren Reparaturauftrag über die Preise für die einzelnen Positionen wieder rein. Das der Kostenvoranschlag, respektive die bezahlte Probearbeit, einen Nebeneffekt hat, ist mehr gewollt als Zufall. Denn welcher Kunde holt sich 5, 10 oder mehr Kostenvoranschläge rein wenn er die alle bezahlen muss? Und zudem setzt hier ein psychologischer Effekt ein. Wenn der Kunde eh schon etwas gezahlt hat, dann ist er eher bereit den Auftrag zu vergeben wenn er die Kosten mit dem Auftrag verrechnet bekommt. So hat der Kunde zumindest nicht den Eindruck er hätte Geld verschenkt.</p>
<h3>Die falsche Kundschaft</h3>
<p>Ich sehe schon wie viele sich jetzt sagen &#8220;<em>Aber meine Kunden würden niemals für eine Probearbeit bezahlen&#8230;</em>&#8220;. Gut, dann sage ich: Du hast die falschen Kunden.<br />
Kunden die bereits an diesen Punkt zu geizig sind die Probearbeit zu bezahlen, machen oft auch bei der Endabrechnung Schwierigkeiten. Man tut sich demnach also kein Gefallen damit monatelang für potentielle Kunden Arbeiten zu erstellen die dann am Ende ohnehin nicht in einem Auftrag enden oder, wenn es doch zu einem Auftrag kommt, endlose Diskussionen über Preise und Leistungen nach sich ziehen. Denn auch der Kampf um das wohlverdiente Geld ist kostenlose Arbeit die nicht bezahlt wird!</p>
<p>Gefragt ist also ein gutes Risikomanagement. Man muss sich bei jeden Kunden aufs Neue fragen wie viel Zeit und damit Geld man in diesen Kunden investieren will. Und wie hoch man das Risiko einschätzt das diese Zeit und damit das Geld verloren gehen kann.<br />
Ich habe oft genug für Kunden stundenlang umsonst gearbeitet, erst vor kurzem war es eine Position von ungefähr 20 Stunden. Dabei hatte ich die ersten 10 Stunden selber zu verschulden weil ich die Situation falsch eingeschätzt hatte. Die Frage ob ich weiterhin Zeit (und Geld) in diesen Kunden investieren will, beantwortete ich für mich mit Ja. Denn zum einen wollte ich den Kunden halten, zum anderen sah ich keine größeren Probleme für zukünftige Aufträge und Zusammenarbeit. Diese 20 Stunden die ich bisher &#8220;umsonst&#8221; gearbeitet habe, sehe ich also nicht als Verlust, sondern als Investition an. Ich konnte dem Kunden so eine Arbeit präsentieren die ihn qualitativ überzeugte und ihn durch diesen &#8220;kostenlosen&#8221; Einsatz von der Leistungsbereitschaft überzeugen. Es ist also eine Investition die man nicht in Heller und Pfennig aufrechnen kann, genauso wenig wie man einen guten Ruf in Euro und Cent umrechnen kann.<br />
Natürlich besteht ein gewisses Risiko das keine weiteren Aufträge kommen oder das sich die spätere Zusammenarbeit sehr schwierig gestaltet. Aber dieses Risiko habe ich als sehr niedrig eingeschätzt.<br />
Es gab aber natürlich auch schon die krassen Gegensätze. Kunden die ich bei den kleinsten Schwierigkeiten habe fallen lassen oder die ich erst gar nicht angenommen habe. Dies ist eine Sache von Bauchgefühl und Erfahrung. Denn aus der wirtschaftlichen Sicht heraus muss man es so sehen: Eine Probearbeit ist eine überschaubare Sache. Ich weiß ich werde x Stunden, oder umgerechnet y Euro, in die Probearbeit investieren. Das Verlustrisiko steht im schlimmsten Fall bei 50%. Entweder man bekommt den Auftrag oder nicht. Dieses Risiko kann man oft genug noch weiter minimieren indem man sich z.B. die Arbeit teilweise bezahlen lässt. Hierdurch steigen die Chancen den Auftrag zu bekommen und das Verlustrisiko sinkt.<br />
Ärger bei der Abrechnung oder bei der Durchführung ist ein unkalkulierbares Risiko. Weder weiß man welche Leistungen noch zusätzlich kommen (meistens sind es dann Sachen die der Kunde in einem Nebensatz nachts um 3 am Telefon erwähnt hat und bei denen der Kunde sich 100%ig sicher ist das dies von Anfang an vereinbart war), man weiß nicht ob und wann das Geld kommt und ob man um die Bezahlung der Position x kämpfen muss. Und vor allem weiß man nicht ob nach so einem Theater noch Folgeaufträge kommen oder ob man überhaupt noch mal Lust hat für diesen Kunden zu arbeiten. Schwierige Kunden sind für mich immer ein 80-100%iges Verlustrisiko. Denn die Zeit die man sich mit ihnen rum ärgert bezahlt einen niemand.</p>
<h3>Fazit</h3>
<p>Probearbeiten und Referenzen gehören zum Geschäftsleben dazu und man kommt um sie nicht herum. Sie sind aber keine Plage die unvorhergesehen über einen herein bricht, sondern in erster Linie eine Investition mit einem gewissen Verlustrisiko. Und selbst dieses Verlustrisiko ist mit etwas Erfahrung überschau- und berechenbar.<br />
Wer ständig mit kostenlosen Probearbeiten konfrontiert wird und häufiger keine Aufträge daraus generieren kann, muss die Verluste auf seine Preise umlegen. Das macht der Metzger genauso wie der Architekt. Nicht umsonst sind die Besten oft die teuersten. Denn sie haben bereits ihr Lehrgeld bezahlt und fahren jetzt die Zinsen ein.<br />
Wenn man jedoch ständig nur zur Probe arbeitet und durch Umlage die Preise in exorbitante Höhe steigen, dann liegt das aller Wahrscheinlichkeit nicht am System, sondern an anderen Faktor. In solch einem Fall sollte man sich fragen ob z.B. die eigene Qualität stimmt oder ob man vielleicht in einem Marktsegment tätig ist das vollkommen überlaufen ist. Unter Umständen hat man aber auch eine Nische besetzt die nicht so ertragreich ist wie man sich erhofft hat.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-25/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Argumentiere niemals mit schlechten Beispielen</title>
		<link>http://yoda.neun12.de/artikel-23</link>
		<comments>http://yoda.neun12.de/artikel-23#comments</comments>
		<pubDate>Sat, 12 Feb 2011 13:34:14 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Selbstständigkeit]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=23</guid>
		<description><![CDATA[Durch meinen Feedreader bin ich über diesen Text von Daniela Warndorf gestolpert. Zuerst dachte ich es sei wieder einer dieser Jammertexte den man von Webworkern in regelmäßigen Abständen zu lesen bekommt. Erst im letzten Absatz las ich das Daniela Warndorf eine Texterin ist und demnach nicht zwangsweise zu der Gruppe der Webworker zu zählen ist. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Durch meinen Feedreader bin ich über <a href="http://offensichtlich.wordpress.com/2011/02/08/von-steaks-autos-und-milch-oder-nein-hier-gibt-es-nichts-umsonst/">diesen Text von Daniela Warndorf</a> gestolpert. Zuerst dachte ich es sei wieder einer dieser Jammertexte den man von Webworkern in regelmäßigen Abständen zu lesen bekommt. Erst im letzten Absatz las ich das Daniela Warndorf eine Texterin ist und demnach nicht zwangsweise zu der Gruppe der Webworker zu zählen ist. Die beschriebene Problematik trifft aber auf viele Bereiche und Berufsgruppen zu.</p>
<p>Im Grunde genommen ist die Grundaussage des Textes, dass man nicht für umsonst arbeiten könne, richtig. Ich denke und hoffe das dies auch niemand macht. Auf der anderen Seite sind Probearbeiten und Referenzen wirklich nichts mit dem man niemals im Berufsleben hätte rechnen können. Dies trifft nicht nur auf Kreative und Webworker zu, sondern auf so ziemlich jeden herstellenden Beruf.<br />
Nehmen wir einmal den Metzger aus Daniela Warndorfs ersten Beispiel. Probieren geht über studieren, dass wussten Metzger und Wurstfachverkaufskräfte schon bevor es Internet gab. Wer kennt es nicht, dass kleine Stückchen Fleischwurst das über die Theke gereicht wird. Oder die kleinen Probierhappen, die oft in jeder guten Metzgerei auf dem Tresen ausliegen. Warndorfs Beispiel ist völlig daneben gegriffen. Zum einen gibt es Proben beim Metzger, zum andern ist ein Steak nicht mit einem Text oder einem anderen digitalen Produkt vergleichbar. Schneidet man von einem Steak ein Stück ab damit der Kunde es probieren kann, ist das Steak irgendwann weg. Einen Text oder ein digitales Produkt wie z.B. ein Stück PHP-Code kann man unendlich oft kopieren und verteilen ohne das seine Qualität leidet oder das es weniger wird. Der Vergleich Texter mit Metzger hinkt also nicht nur, er ist schlichtweg falsch.</p>
<p>Ihr nächstes Beispiel ist da schon etwas besser. Die &#8220;kostenlose&#8221; Reparatur im Autohaus. Sicherlich erhofft sich der eine oder andere Kunde das quasi als Serviceleistung kleinere Mängel gleich mit abgestellt werden. Die Werkstätten haben jedoch schon vor langer Zeit die Situation erkannt und entsprechend gehandelt. Kostenvoranschläge nur gegen Bezahlung und jede Reparatur wird in Rechnung gestellt. Es tat zwar weh als ich mal für den <a href="http://neun12.de/artikel-744/">Austausch einer Sicherung im Wert von 75 Cent 20 Euro</a> bezahlt habe, es war aber gerechtfertigt. Denn bezahlt habe ich nicht die Sicherung, die gab es gratis. Bezahlt habe ich den Zeitaufwand der Werkstatt.</p>
<p>Warndorfs letztes Beispiel ist leider wieder völlig daneben. Denn auch die Milch verbraucht sich, genauso wie ein Steak. Und tatsächlich ist es, juristisch gesehen, das Risiko des Supermarktes die Waren zur Selbstbedienung im Regal anzubieten. Alternativ könnten sie, wie im guten alten Tante Emma-Laden, die Waren in einem Lager deponieren und dem Kunden nur die Waren aushändigen, die er kaufen möchte. Es ist also, wie man als Programmierer sagen würde, ein Designfehler des Supermarktes.</p>
<p>Warenproben, Probearbeiten und Referenzen gehören zum Berufsalltag genauso dazu wie Kunden die Aufträge geben. Ich denke Daniela Warndorf wird sicherlich auch schon mal eine Probefahrt mit dem Auto gemacht haben das sie kaufen wollte. Ich gehe aber nicht davon aus das sie dem Verkäufer deswegen gleich mal 30 Euro für Sprit und Abnutzung des Autos gezahlt hat. Oder? Und wer kennt sie nicht, Kunden die sich im Fachmarkt beraten lassen und dann billig im Internet bestellen?<br />
Natürlich will man ein Produkt erst einmal testen bevor man es kauft. Kann man dies nicht, weil es sich z.B. um ein Steak oder ein Liter Milch handelt, will man als Kunde Referenzen sehen. Also z.B. andere Kunden die mit diesen Produkt zufrieden waren.</p>
<p>Wie eingangs erwähnt, höre oder lese ich immer wieder vor allem von Selbstständigen das sie vollends darüber frustriert sind wenn sie &#8220;kostenlos&#8221; arbeiten sollen. Dabei drängt sich der Ausspruch &#8220;<em>Wasser predigen &#8211; Wein saufen</em>&#8221; jedes mal aufs neue auf. Denn auf der einen Seite würden sie selber niemals etwas kaufen was sie nicht ausgiebig getestet haben oder was zumindest über ausreichend gute Referenzen verfügt. Auf der anderen Seite wollen sie ihre Produkte weder zum Testen noch irgendwelche Referenzen anbieten. Aus diesen Frust heraus werden dann haarsträubende Beispiele konstruiert die nicht einmal ansatzweise das Problem darstellen. Geschweige denn mal Lösungsansätze wie man mit dem Problem &#8220;kostenlose Arbeit&#8221; umgehen soll. Das dieses Problem immer wieder auftaucht, kann leider nicht abgestritten werden.</p>
<p>Auf das Problem &#8220;kostenlose Arbeiten&#8221; gehe ich in einen anderen Artikel mal näher ein, es würde ansonsten den Rahmen sprengen. Es ist jedoch immer wieder schade anzusehen wie hilflos, verzweifelt und unkreativ angeblich selbstständige mit solchen Problemstellungen umgehen. Das da meist nur Jammern übrig bleibt, ist mehr als bedauerlich.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-23/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>1 Million mal</title>
		<link>http://yoda.neun12.de/artikel-13</link>
		<comments>http://yoda.neun12.de/artikel-13#comments</comments>
		<pubDate>Tue, 18 Jan 2011 15:16:47 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=13</guid>
		<description><![CDATA[Hier im Blog ist es derzeit noch recht ruhig was arbeitsbedingtem Zeitmangel geschuldet ist. Dabei ist noch so einiges zu tun um das Blog fit zu bekommen. Kommt Zeit, kommt Blog. Weniger ruhig geht es bei WordPress-Deutschland zu. Die verbuchten dieser Tage den 1 millionsten Download der DE-Version von WordPress. Alle Achtung, dass ist nicht [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Hier im Blog ist es derzeit noch recht ruhig was arbeitsbedingtem Zeitmangel geschuldet ist. Dabei ist noch so einiges zu tun um das Blog fit zu bekommen. Kommt Zeit, kommt Blog.</p>
<p>Weniger ruhig geht es bei <a href="http://blog.wordpress-deutschland.org">WordPress-Deutschland</a> zu. Die verbuchten dieser Tage den 1 millionsten Download der <a href="http://blog.wordpress-deutschland.org/2011/01/14/eine-million-downloads-de-edition-verlosung.html">DE-Version von WordPress</a>. Alle Achtung, dass ist nicht gerade wenig.<br />
Zu solch einem Jubiläum gibt es natürlich auch eine Kleinigkeit zu gewinnen. Einen USB-Stick mit 4GB Speicher, WP-Deutschland Gravur und Flaschenöffner. Ich hoffe ich muss nicht erwähnen das es mir bei der Teilnahme an dem Gewinnspiel weder um den 4GB USB-Stick noch um die Lasergravur geht, ich aber scharf auf den Flaschenöffner bin. Kann man immer gebaruchen, genauso wie ein funktionierendes WordPress.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-13/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress und die Localization</title>
		<link>http://yoda.neun12.de/artikel-6</link>
		<comments>http://yoda.neun12.de/artikel-6#comments</comments>
		<pubDate>Fri, 07 Jan 2011 00:09:56 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Tipps&Tricks]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=6</guid>
		<description><![CDATA[Im Grunde genommen ist es eine feine Sache das man in WordPress Themes und Plugins mit relativ wenig Aufwand in eine andere Sprache übersetzen kann. Man muss lediglich darauf achten beim schreiben des Themes bzw. Plugins die Texte in einem bestimmten Format auszugeben und schon kann man unter zuhilfenahme von Programmen wie z.B. poEdit das [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Im Grunde genommen ist es eine feine Sache das man in WordPress Themes und Plugins mit relativ wenig Aufwand in eine andere Sprache übersetzen kann. Man muss lediglich darauf achten beim schreiben des Themes bzw. Plugins die Texte in einem bestimmten Format auszugeben und schon kann man unter zuhilfenahme von Programmen wie z.B. poEdit das Theme oder Plugin in eine andere Sprache übersetzen ohne es neu zu schreiben.</p>
<p>Im Grunde genommen bedeutet hier aber, dass es nur auf den ersten Blick so einfach zu sein scheint. Wer sich schon mal mit den Thema Übersetzung von Plugins oder Themes beschäftigt hat, der wird früher oder später die vielen kleinen Haken zu spüren bekommen. Einer dieser kleinen Haken ist der, dass WordPress einen grundlegenden Unterschied bei der Einbindung von Übersetzungen macht. <code>load_theme_textdomain()</code> ist, wie der Name vermuten lässt, für Themes zuständig, während <code>load_plugin_textdomain()</code> für Plugins zuständig ist. Es ist schon mal auffällig das es für die gleiche Aufgabe zwei verschiedene Funktionen gibt. Also schauen wir doch mal in die Core-Datei wo der Unterschied bei diesen beiden Funktionen liegt.</p>
<p>Beide Funktionen finden wir in <code>wp-includes/l10n.php</code>. Beide Funktionen erwarten bei ihren Aufruf die Übergabe einer Domain und eines Pfades. Im Falle von <code>load_plugin_textdomain()</code> können sogar zwei verschiedenen Arten von Pfaden angegeben werden, wobei der absolute relative Pfad veraltet ist und nicht mehr verwendet werden sollte.<br />
Bei genauerer Betrachtung fällt aber auf, dass <code>load_theme_textdomain()</code> bei der Pfadangabe deutlich toleranter ist als <code>load_plugin_textdomain()</code>. Ist kein Pafd angegeben, so verwendet <code>load_theme_textdomain()</code> das Verzeichnis des aktuellen Templates als Pfad. Ansonsten könnte man den Pfad auch sonstwohin zeigen lassen, es würde noch funktionieren.<br />
<code>load_plugin_textdomain()</code> ist da etwas strikter. Egal ob man einen Pfad angibt oder nicht, es wird auf jeden Fall <code>WP_PLUGIN_DIR</code>, also der Pfad zum Plugin-Verzeichnis, im Pfad eingefügt.<br />
Mit <code>load_theme_textdomain()</code> könnte man also auch eine Sprachdatei benutzen die in <code>wp-content</code> steht. Mit <code>load_plugin_textdomain()</code> wäre dies unmöglich.</p>
<p>Nun sind Pfadangaben in WordPress eine Sache für sich an die man sich gewöhnen und mit denen man umzugehen lernen kann. Deutlich mehr Kopfschmerzen bereitete mir in der Vergangenheit ein Umstand, den ich irgendwie nicht verstehe. Und zwar das Format nach dem die Sprachdateien benannt werden müssen.<br />
<code>load_theme_textdomain()</code> erwartet das die Sprachdatei lediglich aus der Locale und der Endung .mo besteht. Also z.B. de_DE.mo oder en_EN.mo. <code>load_plugin_textdomain()</code> hingegen erwartet zu der Locale noch zusätzlich die Domaine als Teil des Dateinamens. Die Datei muss also das Format domain-locale.mo haben.<br />
Für mich recht unverständlich und letzten Endes kann dies auch zu Problemen führen. Denn ändert sich im Plugin die Domaine, z.B. nach einem Update, dann können auch die (alten) Sprachdateien nicht mehr geladen werden. Man müsste also entweder alle Domainen im Plugin ändern oder alle Sprachdateien umbennenen.</p>
<p>Aber man sollte auch immer die Vorteile im Auge behalten. Durch die Verwendung der Domaine im Dateinamen der Sprachdatei kann man auf recht einfache Weise z.B. zwischen der Du- und der Sie-Form wechseln. Etwas was für deutsche Übersetzungen ja nicht ganz unerheblich ist.<br />
Dazu legt man einfach z.B. eine Sprachdatei mit den Namen <code>pluginname_du_form-de_DE.mo</code> und eine mit den Dateinamen <code>pluginname_sie_form-de_DE.mo</code> an. Im Plugin selber kann man mittels <code>define('MeinePluginDomain', 'plugin_xx_form');</code> eine Konstante definieren die dann in den Übersetzungsfunktionen verwendet wird (z.B. <code>__('This example text', MeinePluginDomain)</code>). Je nachdem ob xx nun &#8216;du&#8217; oder &#8216;sie&#8217; ist, schaltet man zwischen Du- und Sie-Form um.</p>
<p>Jetzt hat man aber ein Problem mit Sprachen die nicht zwischen Du und Sie unterscheiden. Mit Englisch zum Beispiel. Denn man müsste nun für jede Sprache zwei Sprachdateien anfertigen. Einmal <code>plugin_du_form-en_EN.mo</code> und einmal <code>plugin_sie_form-en_EN.mo</code>.<br />
Das ganze gestaltet sich an diesen Punkt als etwas zu aufwendig um schön zu sein. Es gibt aber auch eine Lösung für das Problem. Wir benötigen ja eigentlich nur eine Funktion die in einem Verzeichnis nachschaut ob es eine Sprachdatei nach dem Format domaine-locale.mo gibt und diese lädt. Gibt es eine solche Sprachdatei nicht, wird halt eine Sprachdatei nach den Format locale.mo geladen. Gibt es diese auch nicht, wird halt nichts übersetzt.<br />
Nebenbei erledigt sich mit solch einer Funktion das leidige Pfad-Problem. Die Sprachdateien können nun quasi an einen beliebigen Ort abgelegt werden. Ob dies Sinn macht, sei an dieser Stelle mal dahin gestellt.</p>
<p>Hier erst einmal die Funktion:</p>
<pre class="brush:php">function flexible_loadtextdomain( $path = false, $domain = 'default' ){

  if( ! $path || ! is_dir( $path ) )

    return false;

  global $l10n;

  $local = get_locale();

  $mofile = $path . $domain . '-' . $local . '.mo';

  // try to find a matching translation

  // first try domain-locale.mo

    if( ! file_exists( $mofile ) ){

    // if not found, try only locale.mo

    $mofile = str_replace( $domain . '-', '', $mofile );

      if( ! file_exists( $mofile ) ){

        // if even this failed, throw an error or do something wired

        return false;

      }

    }

  load_textdomain( $domain, $mofile );

  return isset( $l10n[ $domain ] );

}</pre>
<p>Die Funktion erwartet zwei Parameter. Zum einen eine Pfadangabe und zum anderen den Namen der Domain. Die erste If-Abfrage sorgt dafür das überhaupt ein Pfad übergeben wird und das es auch ein Verzeichnis ist. Anstatt einfach nur false zurück zu geben, kann man hier natürlich noch weitere Spielereien unter bringen indem man z.B. einen Pfad vorgibt (<code>WP_PLUGIN_DIR</code> oder <code>get_template_directory</code>).<br />
Der Rest ist das was ich oben schon beschrieben habe. Erst domain-locale.mo suchen, wenn nicht gefunden locale.mo suchen, wenn das auch nicht gefunden irgendwas verrücktes machen, zum Beispiel false zurück geben.<br />
In der letzten Zeile wird dann noch geprüft ob die Domain erfolgreich geladen wurde. Ab WP 3.0 kann man dies durch <code>return load_textdomain(...)</code> abkürzen. In älteren Versionen muss man diesen kleinen Umweg gehen da <code>load_textdomain</code> keine Erfolgsmeldung zurück gibt.</p>
<p>Der Code der Funktion ist ein wenig abgespeckt. Will man es ganz sauber programmieren, dann würde man noch ein paar Sachen anders machen, z.B. nicht <code>$locale = get_locale();</code> verwenden, sondern <code>$locale = apply_filters( 'xyz', get_locale(), $domain );</code> (xyz = theme_locale oder plugin_locale, je nachdem ob man die Funktion in einem Theme oder Plugin verwendet).</p>
<p>Vielleicht ist dieser Beitrag ja eine kleine Anregung für alle die Themes oder Plugins schreiben.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-6/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Eigenes Menü im Backend anpassen</title>
		<link>http://yoda.neun12.de/artikel-5</link>
		<comments>http://yoda.neun12.de/artikel-5#comments</comments>
		<pubDate>Fri, 07 Jan 2011 00:08:44 +0000</pubDate>
		<dc:creator><![CDATA[Ralf]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Tipps&Tricks]]></category>

		<guid isPermaLink="false">http://yoda.neun12.de/?p=5</guid>
		<description><![CDATA[Eigene Menüs im Backend anlegen ist nicht schwer, WordPress stellt hierzu ausreichend Funktionen zur Verfügung. Jedoch haben diese Menüs einen meiner Ansicht nach unschönen Nachteil. Im Menü selber hat der erste Menüpunkt den gleichen Titel wie das Menü selber. Legt man nun also ein Menü Test an, so steht ganz oben als erster Menüpunkt auch [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Eigene Menüs im Backend anlegen ist nicht schwer, WordPress stellt hierzu ausreichend Funktionen zur Verfügung. Jedoch haben diese Menüs einen meiner Ansicht nach unschönen Nachteil. Im Menü selber hat der erste Menüpunkt den gleichen Titel wie das Menü selber. Legt man nun also ein Menü <em>Test</em> an, so steht ganz oben als erster Menüpunkt auch <em>Test</em> (siehe Grafik). Das sieht nicht nur unschön, sondern auch unprofessionell aus. Schöner wäre es, könnte man den ersten Menüpunkt anders benennen. Bei den von WordPress vorgegebene Menüs ist es schließlich genauso.</p>
<p><img src="http://media.tumblr.com/tumblr_le7vyn7zGX1qf49cl.jpg" alt="" align="right" />Der Codex schweigt sich allerdings ein wenig darüber aus wie man es hin bekommt den ersten Menüpunkt anders zu benennen. Anscheinend gibt es auch keinen &#8220;offiziellen&#8221; Weg um dies zu bewerkstelligen. Mit einen kleinen Kniff geht es dennoch.<br />
Dazu muss man die Variable <code>$submenu</code> global verfügbar machen und kann dann darüber seine Menüpunkte beeinflussen:</p>
<pre class="brush:php"><code>global $submenu;
add_menu_page( $page_title, $menu_title, $capability, $menu_slug, [...] );
$submenu[$menu_slug][0][0] = 'My first Menuoption';</code></pre>
<p>$submenu ist ein Array in dem verschiedene Informationen über das Menü gespeichert sind. Es lohnt sich dieses Array mal ausgeben zu lassen und zu schauen welche Informationen man darin findet.</p>
<p>In Bezug auf die Menüs ist <code>$menu</code> ein weiteres interessantes Array. Bei der Masse an Plugins die im Umlauf sind, kommt es nicht selten vor das auch dementsprechend viele Plugins ein zusätzliches Menü einrichten. Da möchte man vielleicht sein eigenes Menü ein wenig von den anderen abgrenzen. Nichts leichter als das, sogar mit Zusatzwert. Man fügt einfach einen Menü-Seperator ein:</p>
<pre class="brush:php"><code>global $menu;
$menu[] = array( '', 'read', '', '', 'wp-menu-separator' );</code></pre>
<p>Damit wird ein Seperator eingefügt mit dem man die Menüleiste auf- und zuklappen kann. Steht der Code vor <code>add_menu_page()</code>, wird der Seperator vor dem eigenen Menü eingefügt. Steht er dahinter, wird der Seperator dementsprechend dahinter eingefügt.<br />
Mit <code>count($menü)</code> lässt sich recht einfach feststellen wie viele Menüs bereits existieren und ggf. ein Seperator einfügen. Allerdings werden auch sämtliche bereits vorhandenen Seperatoren mitgezählt, dies sollte man beachten und diese ggf. raus rechnen.<br />
Mit <code>'wp-menu-separator-last'</code> erzeugt man übrigens einen &#8220;leeren&#8221; Seperator, also einen Seperator ohne Pfeil wie am Ende des Standard-Menüs von WordPress zu finden ist.</p>
]]></content:encoded>
			<wfw:commentRss>http://yoda.neun12.de/artikel-5/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
