<?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>ROAE-Blog</title>
	<atom:link href="http://blog.root-of-all-evil.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.root-of-all-evil.com</link>
	<description>Studium, Codeing und Gedachtes</description>
	<lastBuildDate>Tue, 18 May 2010 18:09:29 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Punkte in Google-Mail-Adressen</title>
		<link>http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/</link>
		<comments>http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/#comments</comments>
		<pubDate>Tue, 18 May 2010 18:09:29 +0000</pubDate>
		<dc:creator>Felix</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Erklärungen]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[gmail]]></category>
		<category><![CDATA[google]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=797</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Registriert man bei Google Mail eine E-Mailadresse &#8211; z.B. dasIstEineEMailAdresse@googlemail.com &#8211; so wird der komplette Benutzername unabhängig von Groß-/Kleinschreibung und Punkten (.) interpretiert: registriert man die E-Mail-Adresse dasIstEineEMailAdresse@googlemail.com, <strong>so hat man automatisch auch folgende weitere Adressen</strong> das.ist.eine.emailadresse@googlemail.com oder das.istEineEmailAdresse@googlemail.com etc.</p>
<p>Welche <strong>Vorteile sich für den Benutzer</strong> und welche <strong>Nachteile für einen Webmaster</strong> sich hierdurch ergeben soll hier kurz aufgezeigt werden.</p>
<p><span id="more-797"></span></p>
<h2>Vorteile für den Benutzer</h2>
<p>Durch unterschiedliche E-Mail-Adressen, welche aber alle zu einem Postfach führen, gibt es für den Benutzer eine Reihe von Vorteilen.</p>
<h3>Organisation/Kategorisierungen</h3>
<p>Der Benutzer kann die E-Mails besser organisieren bzw. kategorisieren. Beispielsweise wird für alle <strong>Newsletter die E-Mail-Adresse m.ax.mueller@googlemail.com</strong> verwendet, für den <strong>privaten Kontakt max.mueller@googlemail.com.</strong> Mit Hilfe der <strong>Filtereinstellungen bei Google-Mail</strong> können dann alle Newsletter unabhängig vom Absender mit einem Label versehen und aus dem Posteingang verschoben werden &#8211; so findet man im Posteingang dann wirklich nur die &#8220;wichtigen&#8221; E-Mails.</p>
<p><img class="aligncenter size-medium wp-image-804" title="Filter-Einstellungen bei Google-Mail" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/05/screen-capture-300x60.jpg" alt="" width="300" height="60" /></p>
<h3>Privatsphäre</h3>
<p>Manche Plattformen bieten die Möglichkeit über eine E-Mail-Adresse zu prüfen ob ein User angemeldet ist (z.B. Facebook). Dies ist nicht von jedem User gewünscht. Hier bietet sich dann mit einer Google-Mail-Adresse die Möglichkeit seine <strong>E-Mail-Adresse auf der Plattform z.B. auf max.m.u.e.l.l.e.r@googlemail.com zu ändern.</strong> Sucht ein User nach max.mueller@googlemail.com so wird er nicht fündig werden &#8211; und trotzdem muss man nicht eine weitere E-Mail-Adresse registrieren.</p>
<h2>Nachteile für den Webmaster</h2>
<p>Natürlich ergeben Sie durch dieses Feature auch Nachteile. Oft wird die <strong>E-Mail-Adresse als primary Key</strong> verwendet um so Mehrfachanmeldungen oder Nutzungen schnell und einfach zu unterbinden. Dies wird genutzt unter anderem bei:</p>
<ul>
<li>Serialversand<br />
Das Produkt X darf 14 Tage mit der Serial Y getestet werden. Wird danach versucht erneut eine Serial anzufordern, schlägt das für die benutzte Adresse fehl</li>
<li>Unterbinden von Mehrfachregistrierung<br />
Es soll verhindert werden dass sich ein Benutzer mehrfach registriert &#8211; unabhängig ob das Verhalten gewollt oder ungewollt ist. Sei es auf Community-Seiten oder auch finanziell orientierten Homepages wie Online-Shops.</li>
</ul>
<p>Natürlich gibt es neben der &#8220;Google-Mail-Problematik&#8221; auch noch andere Möglichkeiten wie Wegwerf-E-Mail-Adressen, Automatische Erstellung von E-Mail-Konten, oder sogar der Besitz einer (E-Mail-)Domain (ich@meineDomain.de, ich1@meineDomain.de, &#8230;). Wir wollen uns aber hier nur mit Google-Mail beschäftigen.</p>
<p>Eine Möglichkeit das Problem zu umgehen ist der folgende einfache Algorithmus:</p>
<ol>
<li>Prüfe ob Google-Mail-Adresse</li>
<li>Wenn Ja entferne im Benutzername alle Punkte</li>
<li>Speichere E-Mail-Adresse</li>
</ol>
<p>Wenn nun geprüft werden soll, ob ein Account schon eine E-Mail-Adresse verwendet, wird auf diese der Algorithmus angewandt, und dann z.B. die Datenbank nach der Mail-Adresse durchsucht. Bleibt die Suche erfolglos, darf sich der User registrieren, wenn nicht, ist der User wohl mit einer ähnliche Adresse bereits registriert und sollte darauf hingewiesen werden. <strong>Wichtig ist hierbei, dass der Datenbestand, in dem die Mail-Adresse gesucht wird, bereits die &#8220;gesäuberte Adressen&#8221; besitzt.</strong></p>
<p><span style="text-decoration: underline;"><span style="color: #ff0000;">Hinweis:</span></span> Bei Google-Mail sind gmail.com und googlemail.com möglich. Dies wird vom Script entsprechend beachtet und alle Adressen auf gmail.com umgeschrieben.<strong><br />
</strong></p>
<p>Die Implementierung könnte in PHP z.B. gefolgt aussehen:</p>
<pre class="php" title="code">/**
 * Cleans an e-mail-address (especially Google-Mail-adresses) to avoid duplicates.
 * @param String $emailStr e-mail-address
 */
function clean_email($emailStr) {
	$emailStr = strtolower ($emailStr);
	$tokens = preg_split ("/@/", $emailStr);

	if ($tokens [1] == "gmail.com" || $tokens [1] == "googlemail.com") {
		return str_replace (".", "", $tokens [0]) . "@googlemail.com";
	} else {
		return $emailStr;
	}
}</pre>
<p>Ein Aufruf von</p>
<pre class="php" title="code">echo clean_email("m.a.x.MUELLE.R@gmail.com") . "&lt;br/&gt;";
echo clean_email("m.a.x.MUELLE.R@googlemail.com") . "&lt;br/&gt;";
echo clean_email("max.mueller@web.de");</pre>
<p>hat dann entsprechende folgende Ausgabe:</p>
<pre>maxmueller@googlemail.com
maxmueller@googlemail.com
max.mueller@web.de</pre>
<h2>Epilog</h2>
<p>Nicht immer wenn sich ein User versucht nochmal zu registrieren ist dies beabsichtigt. Ggf. hat der User schlicht vergessen dass er auf einer Seite registriert ist, oder kann sich nicht mehr einloggen, weil er seine Logindaten nicht mehr kennt. Deshalb sollte immer eine entsprechende Meldung ausgegeben werden wie z.B. &#8220;Es ist bereits ein User mit dieser E-Mail-Adresse registriert. Haben Sie Ihr Passwort vergessen?&#8221; mit entsprechenden Link zu einer Seite, wo ein neues Passwort beantragt werden kann.</p>
<p>Auch ist bei Prüfung auf Google-Mail-Adressen und Wegwerf-Adressen natürlich nicht vor einer Mehrfachregistrierung geschützt. Sicher ist das nur über das kostenpflichtige und  zeitaufwendige Postident-Verfahren &#8211; was jedoch im Internet an der Praxis scheitert.</p>
<p><strong>Weitere Informationen:</strong></p>
<ul>
<li><a href="http://mail.google.com/support/bin/answer.py?hl=de&amp;answer=10313">Google Mail-Hilfe &#8211; Empfangen von E-Mail-Nachrichten anderer Personen</a></li>
<li><a href="http://bspamfree.org/">bspamfree.org &#8211; Wegwerf-E-Mail-Adressen</a></li>
<li><a href="http://de.wikipedia.org/wiki/Postident">Wikipedia &#8211; Postident</a></li>
<li><a href="http://www.deutschepost.de/dpag?xmlFile=1015469">Deutsche Post &#8211; Identitätsprüfung</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP und Geolocation &#8211; Lokalisierung leicht gemacht</title>
		<link>http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/</link>
		<comments>http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/#comments</comments>
		<pubDate>Sun, 09 May 2010 17:39:03 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[geocity]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[ip]]></category>
		<category><![CDATA[location]]></category>
		<category><![CDATA[maxmind]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=700</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Ein Besuch auf <a href="http://www.regnets.in" target="_blank">www.regnets.in</a> zeigt kurz und knapp die aktuelle Wetterlage für den Standort des Besuchers an. Ermöglicht wird das über Geolocation anhand der IP-Adresse des Benutzers. Natürlich kann der Benutzer auch andere Städte abfragen: <a href="http://www.regnets.in/Hamburg" target="_blank">www.regnets.in/Hamburg</a> oder <a href="http://www.regnets.in/Berlin" target="_blank">www.regnets.in/Berlin</a>.</p>
<p>Geolocation lässt sich beispielsweise mit (kostenlosen) <a href="http://www.maxmind.com/app/geolitecity" target="_blank">GeoLite City von MaxMind</a> sehr einfach in PHP-Projekte integrieren. Mit nur wenigen Dateien und wenigen Zeilen Code lässt sich so der Standort des Besuchers relativ genau (für Deutschland mit<a href="http://www.maxmind.com/app/geolite_city_accuracy" target="_blank"> 71% Trefferquote</a>) bestimmen.<span id="more-700"></span></p>
<p>Die benötigten Dateien&#8230;</p>
<ul>
<li>geoip.inc</li>
<li>geoipcity.inc</li>
<li>geoipregionvars.php</li>
<li>GeoLiteCity.dat &#8211; die eigentliche Datenbank</li>
</ul>
<p>&#8230; lassen sich<a href="http://geolite.maxmind.com/download/geoip/api/php/" target="_blank"> hier herunterladen</a>.</p>
<p>Folgender PHP Code liefert die Geolocation des Besuchers:</p>
<pre class="php" title="code">include("geoipcity.inc");
include("geoipregionvars.php");

function getCity() {

	$gi = geoip_open("GeoLiteCity.dat", GEOIP_STANDARD);
	$record = geoip_record_by_addr($gi, $_SERVER['REMOTE_ADDR']);
	geoip_close($gi);

	return $record-&gt;city;
}</pre>
<p>Das $record-Objekt enthält aber auch noch andere, interessante Daten:</p>
<pre class="php" title="code">print $record-&gt;country_code . " " . $record-&gt;country_code3 . " " . $record-&gt;country_name . "\n";
print $record-&gt;region . " " . $GEOIP_REGION_NAME[$record-&gt;country_code][$record-&gt;region] . "\n";
print $record-&gt;city . "\n";
print $record-&gt;postal_code . "\n";
print $record-&gt;latitude . "\n";
print $record-&gt;longitude . "\n";
print $record-&gt;metro_code . "\n";
print $record-&gt;area_code . "\n";
</pre>
<p>Fragen, Anregungen? Dafür sind die Kommentare da!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Favelizer &#8211; ein 16&#215;16 Pixel Frequenz Spectrum</title>
		<link>http://blog.root-of-all-evil.com/2010/05/favelizer-ein-16-x-16-pixel-frequenz-spectrum/</link>
		<comments>http://blog.root-of-all-evil.com/2010/05/favelizer-ein-16-x-16-pixel-frequenz-spectrum/#comments</comments>
		<pubDate>Mon, 03 May 2010 20:18:34 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[audio]]></category>
		<category><![CDATA[audio tag]]></category>
		<category><![CDATA[equalizer]]></category>
		<category><![CDATA[frequency]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[processing.js]]></category>
		<category><![CDATA[spectrum]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=776</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Mit HTML 5 kommt auch das neue &lt;audio&gt; Element. Es erlaubt das abspielen von Audiodateien direkt im Browser. Eine kleine Gruppe Entwickler bei Mozilla bemühen sich auch um eine Audio API, die das Abfragen und Manipulieren der Audioelemente ermöglichen kann. Noch gilt die API als experimentel, soll aber in zukünftige Firefox Versionen zu finden sein.</p>
<p>Was für Möglichkeiten sich mit dieser API ergeben zeigen <a href="http://vocamus.net/dave/?p=1074" target="_blank">zahlreiche eindrucksvolle Beispiele</a>. Hier ist eines meiner Experimente:<br />
<span id="more-776"></span><br />
Favelizer &#8211; ein 16 x 16 Pixel Frequenz Spectrum in der Browseradresszeile:</p>
<p><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/05/favelizer.jpg" rel="lightbox[776]"><img class="aligncenter size-medium wp-image-777" title="favelizer" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/05/favelizer-300x137.jpg" alt="" width="300" height="137" /></a>Eine Live-Vorschau findet sich hier: <a title="Favelizer" href="http://labs.daslaboratorium.de/favelizer" target="_blank">http://labs.daslaboratorium.de/favelizer</a></p>
<p>Wer sich die gepatchte Firefox-Beta nicht herunterladen möchte, kann den Favelizer auch hier in Aktion ansehen (leider ohne Sound):</p>
<p><object width="680" height="520"><param name="movie" value="http://blog.root-of-all-evil.com/wp-content/uploads/2010/05/Favelizer.swf"><param name="quality" value="high"><param name="bgcolor" value="#FF6633"><embed src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/05/Favelizer.swf" quality="high" type="application/x-shockwave-flash" width="680" height="520"></embed></object></p>
<h3>So funktionierts:</h3>
<p>Das Audio-Element erhält ein onaudiowritten Attribut, mit dem wir beim Abspielen der Musik fortlaufend eine beliebige Javascript Funktion aufrufen können:</p>
<pre class="html" title="code">&lt;audio src="datei.ogg" controls="true" onaudiowritten="update(event);"&gt;</pre>
<p>Mit dem übergebenen event-Objekt können wir auf die Audio-Signale zugreifen:</p>
<pre class="JavaScript" title="code">function update(event) {
     signal   = event.mozFrameBuffer;
     spectrum = event.mozSpectrum;
 }
</pre>
<p>Um das Favicon dynamisch und möglichst schnell erzeugen zu können, greife ich auf processing.js zurück. Das ist wesentlich einfacher als beispielsweise mit</p>
<pre class="JavaScript" title="code">ctx = $('frame').getContext('2d');
ctx.fillStyle = '#000';
ctx.fillRect(1, 1, 1, 16);</pre>
<p>Pixel für Pixel einzeln zu färben.</p>
<p><a href="http://processingjs.org/" target="_blank">Processing.js</a> ist eine durch Javascript verwirklichte eigene Programmiersprache und gleicht in seinem Aufbau Java. Processing.js nutzt das html5 canvas object und ermöglicht beeindruckende Graphikdarstellungen im Browser &#8211; ganz ohne Flash.</p>
<p>Mit einem bisschen Mathematik lässt sich so ganz einfach ein Frequenz Spectrum auf 16 x 16 Pixel bringen:</p>
<pre class="Java" title="code">void draw() {
    background(0);
    strokeWeight(1.0);

    if (spectrum.length &gt; 0) {
        for (int i = 0; i &lt;= 16; i++) {
            var j         = i + 30 * spectrum.item(i * 4);
            var log       = -1 * Math.log(1 / j);
            var magnitude = 16 - spectrum.item(i * 4) * 256 * log;
            stroke(0, 255, 0);
            line(i, 16, i, 16 - magnitude);
        }
    }
}</pre>
<p>Das Favicon brauchen wir dann nur noch in regelmäßigen Abständen auszutauschen:</p>
<pre class="JavaScript" title="code">window.setInterval(function() {
    var icon = $('icon');
    var newIcon = icon.cloneNode(true);
    newIcon.setAttribute('href', ctx.canvas.toDataURL());
    icon.parentNode.replaceChild(newIcon, icon);
 }, 60);
</pre>
<p>Es lohnt sich auf jeden Fall ein Blick in den Sourcecode: <a title="Favelizer" href="http://labs.daslaboratorium.de/favelizer" target="_blank">http://labs.daslaboratorium.de/favelizer</a></p>
<h3>Es stockt bei mir!?</h3>
<p>Das liegt daran, dass wir zum Austausch der einzelnen Favicon-Frames ein neues Objekt erstellen und damit das alte Icon auswechseln müssen. Da häuft sich natürlich einiges an Datenmüll (alte, nicht mehr verwendete Objekte) an, der aufwendig beseitigt werden muss. Der sg. Garbage-Collector verschluckt sich. Eine schnelle Lösung habe ich im Moment leider noch nicht parat.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/05/favelizer-ein-16-x-16-pixel-frequenz-spectrum/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 WebSocket &#8211; Fullduplex Verbindungen mit JavaScript</title>
		<link>http://blog.root-of-all-evil.com/2010/04/html5-websocket-fullduplex-verbindungen-uber-javascript-und-html5/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/html5-websocket-fullduplex-verbindungen-uber-javascript-und-html5/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 13:02:30 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[google chrome]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[pywebsocket]]></category>
		<category><![CDATA[Websocket]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=752</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Das »Web2.0« mit seiner neuen (alten) Technologie Ajax macht viele Webseiten zu komplexen Programmen. Webseiten können zur Laufzeit verändert und mit dynamischen Inhalten gefüttert werden. Doch Ajax ist nicht unbedingt geeignet für jede Webanwendung, da es immer noch zwei Verbindungen benötigt: Einen Up-Stream für die GET- oder POST-Anfrage und einen Downstream für die Response.</p>
<p>Wer beispielsweise schon einmal mit Google Wave gespielt hat, wird merken, dass eingegebene Zeichen (mit sehr kurzer Verzögerung) auf dem Bildschirm des Gesprächspartners erscheinen. Wer so etwas mit Ajax verwirklichen will muss trickreich mit so genannten long live HTTP GET Requests spielen (<a title="Google I/O Wave uses long live HTTP GET Requests" href="http://code.google.com/intl/fr-FR/events/io/2009/sessions/GoogleWavePoweredByGWT.html" target="_blank">siehe diese Google Wave Präsentation</a>, Minute 11:00). Doch Abhilfe naht: das HTML5 WebSocket Interface findet sich bereits in den erstsen Web-Browsers implementiert. Auch der neue <a href="http://dev.w3.org/html5/websockets/" target="_blank">Editor&#8217;s Draft der WebSocketAPI</a> von gestern (15. April 2010) sieht vielversprechend aus.</p>
<p><span id="more-752"></span>Das WebSocket Interface erlaubt den Aufbau einer bestehenden, fullduplex Verbindung über das WebSocket Protokoll und lässt sich erstaunlich einfach mit JavaScript in die eigenen Webanwendungen implementieren. Es besitzt einen Constructor</p>
<pre class="JavaScript" title="code">WebSocket(url [, protocol])</pre>
<p>mit den Parameter URL (String) und dem optionalen Parameter protocoll (String).</p>
<p>Das WebSocket Interface besitzt die folgenden Attribute:</p>
<pre>readonly String URL
readonly unsigned short readyState
    0 = Connecting
    1 = Open
    2 = Closing
    3 = Closed
readonly unsigned long bufferedAmount</pre>
<p>Das WebSocket Interface besitzt zudem die folgenden Funktionen:</p>
<pre>boolean send(String data)
void close()</pre>
<p>Außerdem besitzt das Interface folgende Events:</p>
<pre>onopen
onmessage
onerror
onclose</pre>
<p>Eine typische und rudimentäre Implementierung könnte demnach so aussehen:</p>
<pre class="JavaScript" title="code">var mySocket = new WebSocket('ws://domain.com:80');
mySocket.onopen = function() {
    setInterval(function() {
        mySocket.send('Hello');
    }, 50);
}</pre>
<p>Wir erstellen demnach ein WebSocket Objekt und senden alle 50ms eine Nachricht an den Empfänger auf Port 80. Hier können bereits Probleme auftreten: Was wenn die Verbindung des ausführenden Rechners nicht ausreicht um alle 50ms eine Nachricht zu versenden? Hierfür nehmen wir das Attribut bufferedAmount zur Hilfe. bufferedAmount ist nur dann gleich 0, wenn keine Daten mehr in der Warteschlange zum Versenden sind. Unsere überarbeitete Implementierung:</p>
<pre class="JavaScript" title="code">var mySocket = new WebSocket('ws://domain.com:80');
mySocket.onopen = function() {
    setInterval(function() {
        if(mySocket.bufferedAmount == 0) {
            mySocket.send('Hello');
        }
    }, 50);
}
</pre>
<p>Empfangene Daten können wir mit dem onmessage Event verarbeiten:</p>
<pre class="JavaScript" title="code">mySocket.onmessage = function(data) {
    document.getElementById('Output').appendChild(
        document.createTextNode(data)
    );
}
</pre>
<p>Hier zeigen wir die Daten im Browserfenster an. Fehler können wir mit dem error Event abfangen. Das Auslösen des error Events muss nicht zwangsläufig bedeuten, dass die Verbindung abgebrochen ist, kann es aber:</p>
<pre class="JavaScript" title="code">mySocket.onerror = function(error) {
    alert('error');
    if(mySocket.readyState != 1) {
        document.getElementById('Output').appendChild(
            document.createTextNode("Ein Fehler ist aufgetreten.")
        );
    }
}
</pre>
<p>Um Daten mit einem Server auszutauschen benötigen wir einen Server, der das WebSocket-Protokoll unterstützt. Für die ersten Experimente eignet sich zum Beispiel <a href="http://code.google.com/p/pywebsocket/" target="_blank">pywebsocket</a>. Es kann sowohl als Apache-Modul oder auch als Stand-Alone-Server betrieben werden.</p>
<p>Im Moment unterstützen meines Wissens leider nur Google Chrome und Safari WebSocket. Firefox wird es in nicht alzu ferner Zukunft implementieren. <a href="http://bloga.jp/ws/jq/conn/wsdemo2.htm" target="_blank">Eine Chat-Demo unter Verwendung von WebSockets findet sich hier</a>.</p>
<p>Anregungen? Hierfür sind die Kommentare da.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/html5-websocket-fullduplex-verbindungen-uber-javascript-und-html5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>fsockopen statt file_get_contents für HTTP-Requests</title>
		<link>http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 13:00:05 +0000</pubDate>
		<dc:creator>Felix</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Erklärungen]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[post]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=737</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Bei zahlreichen Webspace-Providern ist die Funktionalität von file_get_contents für http deaktiviert. Es treten dann Fehlermeldungen wie</p>
<p><em>Warning: file_get_contents() [function.file-get-contents]: URL file-access is disabled in the server configuration in [..] </em>und<em></em></p>
<p><em>Warning: file_get_contents([..]) [function.file-get-contents]: failed to open stream: no suitable wrapper could be found in [..]</em></p>
<p>auf. Mit folgender Funktion kann man das Verhalten von file_get_contents jedoch emulieren.</p>
<p><span id="more-737"></span></p>
<p>Die Funktion <em>get_contents_by_uri</em> benutzt <em>fsockopen </em>um Inhalte zu laden:</p>
<pre class="php" title="code">/**
 * Gets contents of any webside by an URI.
 * @author http://blog.root-of-all-evil.com
 * @param String $uri
 */
function get_contents_by_uri($uri) {
	$uriElem = parse_url ( $uri );
	$fp = @fsockopen ( $uriElem ['host'], 80, $errno, $errstr, 10 );

	if (! $fp) {
		throw new Exception ( "Could not create socket: '" . $errnstr . "' (" . $errno . ")." );
	}

	$request = "GET " . $uriElem ['path'] . (isset ( $uriElem ['query'] ) ? "?" . $uriElem ['query'] : "") . " HTTP/1.1\r\n";
	$request .= "Host: " . $uriElem ['host'] . "\r\n";
	$request .= "Connection: Close\r\n\r\n";

	fwrite ( $fp, $request );
	$response = "";
	while ( ! feof ( $fp ) ) {
		$response .= fgets ( $fp, 128 );
	}
	fclose ( $fp );

	// split headers from data
	$responseSplit = explode ( "\r\n\r\n", $response, 2 );

	return $responseSplit [1];
}</pre>
<h1>Erklärung</h1>
<p><strong>Socket erstellen</strong></p>
<p>Die übergebene URI wird über die PHP-Funktion<em> parse_url</em> in ihre Teilmengen zerlegt. Anschließend wird ein Socket erstellt für den entsprechenden Host, auf Port 80 mit einem Timeout von 10 Sekunden. Fehlermeldungen unterdrücken wir durch das <em>@</em>, da wir explizit nach Erfolg abfragen und ggf. eine Ausnahme werfen.</p>
<p><strong>Anfrage schicken, Antwort lesen</strong></p>
<p>Danach wird über <em>fwrite </em>eine entsprechende GET-Anfrage geschickt, welche auch &#8211; falls vorhanden &#8211; die Parameter der URI und nicht nur den Pfad beachtet. Die darauffolgende Antwort wird in die Variable <em>response </em>gelesen.</p>
<p><strong>Antwort bearbeiten und zurückgeben</strong></p>
<p>Da diese neben dem eigentlichen Inhalt auch den Antwortheader enthält wird die Funktion <em>explode </em>benutzt um den Inhalt davon abzuschneiden. Durch den dritten Parameter bei <em>explode </em>mit dem Wert 2, stellen wir sicher, dass nur einmalig an Hand einer Leerzeile getrennt wird wie Sie sich zwischen Antwort-Header und HTML-Code befindet und nicht mehrmals, da natürlich auch Leerzeilen im HTML-Code selbst auftreten können.</p>
<p>Das zweite Element von <em>responseSplit </em>wird zurückgegeben, <em>responseSplit[0]</em> würde entsprechend den Antwort-Header liefern.</p>
<h1>Beispielaufrufe</h1>
<p>Der einfachste Aufruf:</p>
<pre class="php" title="code">echo get_contents_by_uri ( 'http://www.google.de/' );</pre>
<p>Aufruf mit Parametern:</p>
<pre class="php" title="code">echo get_contents_by_uri ( 'http://www.google.de/search?hl=de&amp;site=&amp;q=test' );</pre>
<p>Aufruf mit Ausnahmebehandlung:</p>
<pre class="php" title="code">try{
    echo get_contents_by_uri ( 'http://does_not_exist_.com/bogus.html' );
}
catch(Exception $e)
{
    echo "get_contents_by_uri: ".$e-&gt;getMessage();
}</pre>
<p>Weitere Links:</p>
<ul>
<li><a href="http://de2.php.net/parse_url">PHP &#8211; Funktion parse_url</a></li>
<li><a href="http://de2.php.net/fsockopen">PHP &#8211; Funktion file_get_contens</a></li>
<li><a href="http://de2.php.net/fsockopen">PHP &#8211; Funktion fsockopen</a></li>
<li><a href="http://de2.php.net/manual/en/function.explode.php">PHP &#8211; Funktion explode</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Verlinkbare AJAX-Seiten für Favoriten &#8211; Parameterübergabe in Ankern</title>
		<link>http://blog.root-of-all-evil.com/2010/04/verlinkbare-ajax-seiten-fur-favoriten-parameterubergabe-in-ankern/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/verlinkbare-ajax-seiten-fur-favoriten-parameterubergabe-in-ankern/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 17:06:03 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Erklärungen]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Bookmarks]]></category>
		<category><![CDATA[Favoriten]]></category>
		<category><![CDATA[Parameter]]></category>
		<category><![CDATA[verlinkbare Ajax-Seiten]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=164</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Webseiten sind in Zeiten des Web2.0 häufig komplexe, dynamische Programme. Große Soziale Netze wie Facebook machen es vor: Das Wechseln von einer Seite zur nächsten mittels Links hat ausgedient. Vielmehr werden die Inhalte dynamisch mit Hilfe von Ajax-Anfragen in das Layout geladen. Damit ergeben sich aber auch einige Nachteile.</p>
<p>Einer der Hauptkritikpunkte von Ajax ist die fehlende native Unterstützung von Bookmarks &#8211; also Favoriten. Dynamisch durch Javascript und Ajax veränderte Webseiten lassen sich nicht in ihrem momentanen Zustand zu den Favoriten hinzufügen, da lediglich die URL der Seite nicht aber die vorangegangen Aktionen des Benutzers die zum momentanen Zustand der Seite geführt haben, gespeichert werden. Gleiches gilt für das Verlinken auf solche dynamischen Seiten &#8211; in Zeiten des Austausch von Links in sozialen Netzwerken ein nicht zu unterschätzendes Defizit.</p>
<p>Dabei gibt es Wege und Möglichkeiten auch Javascript mit Parametern über eine URL zu versorgen. Ein simples Beispiel soll hier gezeigt werden.<br />
<span id="more-164"></span></p>
<h3>Javascript Parameter mittels Anker</h3>
<p>Anker, das ist der letzte Teil einer jeden URL, beginnend mit einem Raute- oder auch Hash-Zeichen(#). Anker verweisen auf &#8216;Lesezeichen&#8217; auf der Webseite und dienen dazu, beim Verlinken verticale Lesezeichen anzuspringen oder auch um innerhalb einer Webseite vertikal zu springen.</p>
<p>Der große Vorteil von Ankern für Javascript Parameter ist die Möglichkeit diese durch Javascript zur Laufzeit zu verändern ohne, dass die Webseite verlassen wird. Es ist also möglich die Anker jederzeit durch Javascript ohne Einschränkung des Benutzers zu bearbeiten. Anker können über den Zugriff auf die Eigenschaft</p>
<pre>window.location.hash</pre>
<p>ausgelesen und verändert werden.</p>
<h3>Mehrere Javascript Parameter im Anker unterbringen</h3>
<p>Wie bei gewöhnlichen GET-Parametern, müssen auch mehrere Javascript Parameter möglich sein. GET-Parameter verwenden zur Trennung ein &amp; Zeichen:</p>
<pre>http://url.de?parameter1=wert1<strong><span style="color: #ff0000;">&amp;</span></strong>parameter2=wert2</pre>
<p>Auch bei Javascript-Parametern kann ein &amp;-Zeichen verwendet werden um Parameter von einander zu trennen. Ich persönlich schlage aber das ~-Zeichen vor um Javascript-Parameter von den gewöhnlichen GET-Parametern auch optisch zu trennen.</p>
<pre>http://url.de?parameter1=wert1<strong>&amp;</strong>parameter2=wert2#javascript1=jswert1<strong><span style="color: #ff0000;">~</span></strong>javascript2=jswert2</pre>
<p>Selbstverständlich ist dies nur Geschmackssache und ist abhängig von der jeweiligen verwendeten Implementierung.</p>
<h3>Javascript Parameter auslesen</h3>
<p>Um die mitgegebenen Parameter auszulesen nutze ich die folgende rudimentäre Implementierung:</p>
<pre class="JavaScript" title="code">function getParams() {
	var params = window.location.hash.replace('#', '').split('~');
	var toReturn = new Array();
	for(var i = 0; i &lt; params.length; i++) {
		var find = /^(\w+)(=(.*))?$/;
		find.exec(params[i]);
		toReturn[RegExp.$1] = RegExp.$3;
	}
	return toReturn;
}</pre>
<p>Die Funktion wird beim Start ausgeführt und gibt ein assoziatives Array ähnlich der $_GET und $_POST Arrays in PHP zurück. Selbstverständlich ist auch eine objekt-orientierte Implementierung möglich (ja, richtig gehört, Javascript kann sehr wohl objektorientiert sein).</p>
<h3>Javascript Parameter setzen und löschen</h3>
<p>Javascript Parameter können wie folgt gesetzt werden:</p>
<pre class="JavaScript" title="code">function setParam(param, value) {
	var paramtxt = (value) ? param + '=' + value : param; // Parameter zusammensetzen
	var params = window.location.hash.replace('#', '').split('~');
	var location = params.findParam(param);
	if(location === false)
		// wenn der Parameter noch nicht gesetzt war, setzen wir den Parameter neu
		params.push(paramtxt);
	else
		// war der Parameter schon gesetzt ändern wir ihn entsprechend ab
		params[location] = paramtxt;
	window.location.hash = params.join('~');
}

/* Array.findParam(needle) Hilfsfunktion - ähneld einer in_array Funktion */
Array.prototype.findParam = function(needle) {
	for(var i = 0; i &lt; this.length; i++)
		if(this[i].search('^' + needle + '(=.*)?$') != -1) return i;
	return false;
}</pre>
<p>Parameter lassen sich so ohne Probleme neu setzen oder überschreiben. Um Parameter komplett zu löschen eignet sich folgende Funktion:</p>
<pre class="JavaScript" title="code">function removeParam(param) {
	var params = window.location.hash.split('~');
	var location = params.findParam(param);
	if(location !== false)
		params.splice(location, location);
	window.location.hash = params.join('~');
}</pre>
<p>Mit diesen drei Funktionen können wir also Javascript Parameter im Anker auslesen, setzen und löschen. Das sollte für eine einfache Implementierung reichen.</p>
<h3>Beispiel</h3>
<p>Ein einfaches Anwendungsbeispiel findet sich hier: <a title="Javascript Parameter in Anker für verlinkbare Ajax-Webseiten" href="http://labs.daslaboratorium.de/roae/jsparams.html" target="_blank">http://labs.daslaboratorium.de/roae/jsparams.html</a></p>
<p>Mein erster Gehversuch mit JavaScript Parameter findet sich auf <a title="Regnets.in/deinerStadt?" href="http://www.regnets.in" target="_blank">www.regnets.in</a> implementiert.</p>
<p>Kommentare oder Anregungen? Dafür gibt es die Möglichkeit zum Kommentieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/verlinkbare-ajax-seiten-fur-favoriten-parameterubergabe-in-ankern/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Validierung von URLs mit Zend_Validate</title>
		<link>http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 09:45:38 +0000</pubDate>
		<dc:creator>Felix</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Url erreichbar]]></category>
		<category><![CDATA[Url prüfen]]></category>
		<category><![CDATA[Validator]]></category>
		<category><![CDATA[Validierung]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend Validator]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=701</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Dieser Beitrag beschreibt kurz wie man mit Zend_Validate prüfen kann, ob eine <strong>URL / URI syntaktisch korrekt und die verlinkte Ressource erreichbar ist</strong>.</p>
<p><span id="more-701"></span></p>
<h3>Aufbau einer URL prüfen</h3>
<p>Ein eigenständiger Validator ist im Zend Framework nicht vorhanden welcher den Aufbau einer URL / URI prüft. Jedoch bietet <strong>Zend_URI mit der Methode check()</strong> hier eine entsprechende Funktionalität an. Auf diese greifen wir in einem selbst geschriebenen Validator zurück:</p>
<pre class="php" title="code">class Validator_Uri extends Zend_Validate_Abstract {
	const INVALID_URI = 'invalidUri';
	protected $_messageTemplates = array (self::INVALID_URI =&gt; "'%value%' is not a valid URI" );

	public function isValid($value) {
		$this-&gt;_setValue ($value);

		if (! Zend_Uri::check ($value)) {
			$this-&gt;_error ( self::INVALID_URI );
			return false;
		}

		return true;
	}
}</pre>
<h3>Erreichbarkeit einer URL prüfen</h3>
<p>Es ist nicht nur wichtig, dass die Ressourcenangabe in Ihrem Aufbau korrekt ist, sondern auch, dass sich dahinter ein erreichbares Objekt (Webseite, Bild, PDF etc.) befindet.<br />
Mit Hilfe der PHP-Funktion <strong>get_headers prüfen wir den Antwort-Header</strong> auf Erfolg (200 OK). Mit Hilfe des <strong>@ unterdrücken wir eine Fehler-Ausgabe</strong> von PHP, wenn der String an get_headers keine URL sein sollte:</p>
<pre class="php" title="code">class Validator_Uri_Reachable extends Zend_Validate_Abstract {
	const URI_REACHABLE = 'uriReachable';
	protected $_messageTemplates = array (self::URI_REACHABLE =&gt; "'%value%' not reachable" );

	public function isValid($value) {
		$this-&gt;_setValue ( $value );

		$headers = @get_headers ( $value );
		if (! preg_match ( '/200 OK/', $headers [0] )) {
			$this-&gt;_error ();
			return false;
		}

		return true;
	}
}</pre>
<p>Entsprechend kann dann in einem Zend-Formular z.B. eine Homepageadresse geprüft werden:</p>
<pre class="php" title="code">$hpUrl = new Zend_Form_Element_Text ( 'hpUrl' );
$hpUrl-&gt;setLabel ( 'Homepage:' )
			  -&gt;setDescription ( 'Ihre Homepage- oder Blog-Adresse' );
			  -&gt;setAttrib ( 'value', 'http://' )
			  -&gt;addValidator ( new Validator_Uri() )
			  -&gt;addValidator ( new Validator_Uri_Reachable() )</pre>
<p>Weitere Links:</p>
<ul>
<li><a href="http://framework.zend.com/manual/de/zend.validate.writing_validators.html">Zend Framework Dokumentation &#8211; Schreiben von Prüfern</a></li>
<li><a href="http://usphp.com/manual/en/function.get-headers.php">PHP &#8211; Funktion getheaders</a></li>
<li><a href="http://de.wikipedia.org/wiki/URL#Aufbau">Wikipedia &#8211; Aufbau einer URL</a></li>
<li><a href="http://blog.root-of-all-evil.com/2010/03/tools-fur-regex-regulare-ausdrucke/">ROAE Blog &#8211; Tools für Regex / Reguläre Ausdrücke</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>eBay Trading API Codeschnipsel &#8211; Teil 1</title>
		<link>http://blog.root-of-all-evil.com/2010/04/ebay-trading-api-codeschnipsel-teil-1/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/ebay-trading-api-codeschnipsel-teil-1/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 16:54:55 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[duration time format]]></category>
		<category><![CDATA[eBay]]></category>
		<category><![CDATA[ISO 8601]]></category>
		<category><![CDATA[trading]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=343</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<h3>Converting ISO 8601 Duration Time Format in PHP</h3>
<p>This little sniplet will enable you to parse ISO 8601 Period (Duration) Time Formats into seconds. The ISO 8601 Period is represented by a string like PnYnMnDTnHnMnS where nY represents the number of years, nM the months, nD the days, nH the hours, nM the Minutes and nS the Seconds. The T seperates the date (to the left of the T) from the time (to the right of the T). Parsing this string is quite tricky, because unneeded values (like years and months for durations shorter than a month) can be left out. Parsing is also tricky, because we face two &#8220;M&#8221; as identifiers (for months and Minutes).</p>
<p><span id="more-343"></span></p>
<pre class="php" title="code">// ISO 8601 period format PnYnMnDTnHnMnS (e.g., P0Y0M2DT23H32M51S) to seconds
function calcFinishTime($timeleft)
{
	$parsed = array('Y' =&gt; 0, 'Mo' =&gt; 0, 'D' =&gt; 0, 'H' =&gt; 0, 'Mi' =&gt; 0, 'S' =&gt; 0);
	$calcSec = array(12, 31, 24, 60, 60);
	$toParse = preg_split('/T/', strtoupper($timeleft));
	foreach($toParse as $i =&gt; $array) {
		$split = preg_split('/([PYMDTHMS]+)/', $array, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
		$count = count($split);
		for($j = 0; $j &lt; $count; $j++) {
			if(is_numeric($split[$j])) {
				$type = substr($split[$j +1], 0, 1);
				if($i &lt; 1 &amp;&amp; $type == 'M')	$type = 'Mo';
				elseif($type == 'M')		$type = 'Mi';
				$parsed[$type] += (int)$split[$j];
			}
		}
	}
	$k = 0;
	foreach($parsed as $type) {
		$sec = 1;
		for($j = $k; $j &lt; 5; $j++) {
			$sec *= $calcSec[$j];
		}
		$parsed['seconds'] += $sec * $type;
		$k++;
	}
	$parsed['endtime'] = time() + $parsed['seconds'];
	return $parsed;
}</pre>
<p>A input of &#8220;P2DT3H6M24&#8243; will result in an array like this:</p>
<pre class="php" title="code">array(8) {
  ["Y"]=&gt;
  int(0)
  ["Mo"]=&gt;
  int(0)
  ["D"]=&gt;
  int(2)
  ["H"]=&gt;
  int(3)
  ["Mi"]=&gt;
  int(6)
  ["S"]=&gt;
  int(24)
  ["seconds"]=&gt;
  int(183984)
  ["endtime"]=&gt;
  int(1270843142)
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/ebay-trading-api-codeschnipsel-teil-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automatisieren: Stapelverarbeitung mit Adobe Photoshop</title>
		<link>http://blog.root-of-all-evil.com/2010/04/automatisieren-stapelverarbeitung-mit-adobe-photoshop/</link>
		<comments>http://blog.root-of-all-evil.com/2010/04/automatisieren-stapelverarbeitung-mit-adobe-photoshop/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 10:00:52 +0000</pubDate>
		<dc:creator>Felix</dc:creator>
				<category><![CDATA[Erklärungen]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Adobe Photoshop]]></category>
		<category><![CDATA[Automatisierung]]></category>
		<category><![CDATA[Photoshop]]></category>
		<category><![CDATA[Stapelverarbeitung]]></category>
		<category><![CDATA[Thumbnails generieren]]></category>
		<category><![CDATA[Wasserzeichen]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=562</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Oft fallen im Bereich der Bildbearbeitung Aufgaben an, die für eine Fülle von Bildern wiederholt werden muss. Hierfür bietet Adobe Photoshop die Stapelverarbeitung welche beliebige Schritte, z.B. für einen Ordner mit Bildern, wiederholt.</p>
<p>Hier sollen die notwendigen Schritte aufgezeigt werden, welche es benötigt, <strong>von Bildern automatisiert Thumbnails zu generieren. Das Orginal-Bild und das Thumbnail (Vorschaubild) sollen beide über ein Wasserzeichen geschützt sein.</strong></p>
<p><strong>Sämtliche <a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ROAE-Tutorial-Stapelverarbeitung-mit-Photoshop.zip">benötigten Dateien</a> werden zur Verfügung gestellt</strong>, sodass die notwendigen Schritte gut nachvollzogen werden können.</p>
<p><span id="more-562"></span></p>
<h3>Das Ziel</h3>
<p>Das Video zeigt auf, wie Sie die Aktion, welche wir erstellen, anwenden. Natürlich könne Sie jedes beliebige Wasserzeichen verwenden.</p>
<p style="text-align: center;"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="640" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/l4W2zlZZvUo&amp;hl=de_DE&amp;fs=1&amp;rel=0&amp;color1=0x2b405b&amp;color2=0x6b8ab6" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="640" height="385" src="http://www.youtube.com/v/l4W2zlZZvUo&amp;hl=de_DE&amp;fs=1&amp;rel=0&amp;color1=0x2b405b&amp;color2=0x6b8ab6" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p style="text-align: center;">
<h3>Vorbereitung</h3>
<p>Die <a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ROAE-Tutorial-Stapelverarbeitung-mit-Photoshop.zip">benötigten Dateien laden Sie bitte hier herunter. </a>Nachdem Sie diese auf dem Desktop entpackt haben, starten Sie bitte <em>Adobe Photoshop.</em></p>
<p>Zu Beginn starten wie Adobe Photoshop und  blenden die <strong>Aktionen-Palette</strong> über <em>Fenster -&gt; Aktionen</em> oder die Funktionstaste <em>F9 </em>ein.</p>
<div id="attachment_574" class="wp-caption aligncenter" style="width: 360px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/Photoshop-Aktionen1.png" rel="lightbox[562]"><img class="size-full wp-image-574" title="Photoshop Aktionen" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/Photoshop-Aktionen1.png" alt="" width="350" height="216" /></a><p class="wp-caption-text">Aufrufen der Aktionen-Palette</p></div>
<p>Im rechten Teil verfügen wir nun über die Aktionen-Palette.</p>
<div id="attachment_576" class="wp-caption aligncenter" style="width: 304px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ps_aktionen.png" rel="lightbox[562]"><img class="size-full wp-image-576" title="ps_aktionen" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ps_aktionen.png" alt="" width="294" height="298" /></a><p class="wp-caption-text">Aktionen-Palette</p></div>
<p>Mit Hilfe dieser Palette können wir (fast) alles was wir in Photoshop ausführen aufzeichen lassen und dann später durch einen Knopfdruck abspielen.</p>
<p>Darüber hinaus <strong>öffnen Sie bitte die Datei ROAE-Tutorial/logo.gif </strong>(Sie können später die Datei durch Ihr eigenes Logo ersetzen).<strong><br />
</strong></p>
<h3>Aktion erstellen und aufzeichen</h3>
<p><span style="color: #ff0000;">Hinweis:</span> In den  Dateien ist bereits die fertige Aktion enthalten (Datei <em>Wasserzeichen_und_Thumbnaill-Photoshop_Aktion</em>), welche Sie durch einen  Doppelklick in Photoshop importieren können. Sie müssen also nicht die  folgenden Schritte nicht durchführen.<em><br />
</em></p>
<p>Aus je einem Orginal-Bild sollen zwei Bilder erstellt werden. Einmal das Bild mit einem Wasserzeichen versehen, zum zweiten ein Thumbnail (Vorschaubild), mit einer Breite von 400 Pixeln und ebenfalls einem Wasserzeichen.</p>
<p><strong>Schritt 01: Aktion erstellen</strong></p>
<p>Um die notwendigen Schritte aufzeichnen zu lassen, klicken wir in der Aktionen-Palette auf <em>Neue Aktion erstellen</em></p>
<div id="attachment_577" class="wp-caption aligncenter" style="width: 229px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/neue_aktion_erstellen.png" rel="lightbox[562]"><img class="size-full wp-image-577" title="neue_aktion_erstellen" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/neue_aktion_erstellen.png" alt="" width="219" height="58" /></a><p class="wp-caption-text">Erstellen einer neuen Aktion</p></div>
<p>und tragen in dem neuen Fenster als Aktionsname <em>Wasserzeichen und Thumbnail</em> ein.</p>
<div id="attachment_623" class="wp-caption aligncenter" style="width: 469px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aktion_aufzeichnen.jpg" rel="lightbox[562]"><img class="size-full wp-image-623" title="aktion_aufzeichnen" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aktion_aufzeichnen.jpg" alt="" width="459" height="164" /></a><p class="wp-caption-text">Neue Aktion erstellen &amp; aufzeichnen beginnen</p></div>
<p>Rechts in der Aktionenpalette ist nun der Eintrag <em>Wasserzeichen und Thumbnail</em> markiert. Des weiteren ist der Aufnahme-Knopf aktiviert:</p>
<div id="attachment_580" class="wp-caption aligncenter" style="width: 300px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aktion-läuft.png" rel="lightbox[562]"><img class="size-full wp-image-580" title="aktion läuft" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aktion-läuft.png" alt="" width="290" height="135" /></a><p class="wp-caption-text">Aktionen werden aufgezeichnet</p></div>
<p><strong>Bitte achten Sie darauf folgende Schritte genau wie beschrieben auszuführen</strong>. Kleine Abweichungen (z.B. wechseln zwischen den geöffneten Dateien) können später zu einem Fehler führen.</p>
<p><strong>Schritt 02: Orginal-Bild öffnen</strong></p>
<p>Über <em>Datei -&gt; Öffnen</em> laden wir zuerst eines unser Original-Bilder in Photoshop. Wählen Sie die Datei <em>ROAE-Tutorial/original_bilder/auto.jpg</em> aus dem entpackten ZIP-Archiv (hier können Sie die Bilder nochmal herunterladen &#8211; falls Sie dass noch nicht gemacht haben sollten).</p>
<p><strong>Schritt 03: Hintergrund in Ebene wandeln</strong></p>
<p>Betrachten Sie nun die Ebenenpalette rechts unten (sollte diese nicht sichtbar sein, so blenden Sie diese bitte über<em> Fenster -&gt; Ebenen</em> bzw. die Funktionstaste <em>F7 </em>ein). Klicken Sie mit der rechten Maustaste in der Ebenpalette auf die Ebene <em>Hintergrund </em>und wählen Sie im Kontexmenü <em>Ebene aus Hintergrund</em>. Durch diese Aktion wird die Ebene zu einer &#8220;gewöhnlichen&#8221; Ebene, was mehr Operationen gestartet.<br />
Den Dialog  welcher Name, Farbe und ähnliches abfragt, bestätigen Sie mit <em>ENTER</em>, d.h. die Standardeinstellungen werden übernommen.</p>
<div id="attachment_581" class="wp-caption aligncenter" style="width: 313px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ebene_aus_hintergrund.jpg" rel="lightbox[562]"><img class="size-full wp-image-581" title="ebene_aus_hintergrund" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ebene_aus_hintergrund.jpg" alt="" width="303" height="192" /></a><p class="wp-caption-text">In Ebene wandeln</p></div>
<p>Die Ebene heißt danach nicht mehr <em>Hintergrund</em>, sondern <em>Ebene 0</em> oder ähnlich.</p>
<p><strong>Schritt 04: Wasserzeichen kopieren</strong></p>
<p>Wechseln Sie nun auf das zuvor bereits geöffnete Datei <em>logo.gif</em>.</p>
<p>Wählen Sie nun das komplette Bild aus, in dem Sie über das Menü <em>Auswahl -&gt; Alles auswählen</em> oder die Tastenkombination <em>STRG + A</em> verwenden. Kopieren Sie das Wasserzeichen in die Zwischenablage über das Menü <em>Bearbeiten -&gt; Kopieren</em> (alternativ <em>STRG + C</em>).</p>
<p><strong>Schritt 05: Wasserzeichen in Orginal-Bild einfügen</strong></p>
<p>Wechseln Sie nun zurück auf das Original-Bild mit dem Auto und fügen dort das Wasserzeichen über <em>Bearbeiten -&gt; Einfügen</em> (alternativ <em>STRG + V</em>) ein. Das Wasserzeichen wurde nun zentriert eingefügt und in der Ebenen-Palette eine neue Ebene erstellt:</p>
<div id="attachment_582" class="wp-caption aligncenter" style="width: 620px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ebeneundbild.jpg" rel="lightbox[562]"><img class="size-full wp-image-582" title="ebeneundbild" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/ebeneundbild.jpg" alt="" width="610" height="323" /></a><p class="wp-caption-text">Zentriertes Logo und neue Ebene</p></div>
<p><strong>Schritt 06: Wasserzeichen ausrichten</strong></p>
<p>Kontrollieren Sie dass in der Ebenen-Palette die Ebene, mit dem Wasserzeichen markiert ist (Ebene 1). Markieren Sie nun wieder alles über <em>Auswahl -&gt; Alles auswählen</em> bzw. <em>STRG + A</em>. Richten Sie nun das Logo aus in dem Sie im Menü <em>Ebene -&gt; Ebenen an Auswahl ausrichten -&gt; Untere Kanten</em> und <em>Ebene -&gt; Ebenen an Auswahl ausrichten -&gt; Rechte Kanten</em> wählen. Das Wasserzeichen befindet sich nun bündig unten rechts.</p>
<div id="attachment_584" class="wp-caption aligncenter" style="width: 439px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/logo-ausgerichtet.jpg" rel="lightbox[562]"><img class="size-full wp-image-584" title="logo ausgerichtet" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/logo-ausgerichtet.jpg" alt="" width="429" height="323" /></a><p class="wp-caption-text">Wasserzeichen ausgerichtet</p></div>
<p><strong>Schritt 07: Bild mit Wasserzeichen speichern</strong></p>
<p>Über Datei -&gt; Speichern unter speichern wir das Bild  in dem Ordner <em>roae_tutorial/fertige_bilder/orginal_Bild.jpg.</em> Wählen Sie hierzu bei <strong>Format <em>JPEG</em><em> (*.JPEG, *.JPG, *.JPE)</em></strong>.</p>
<p><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/als_jpg_speichern.jpg" rel="lightbox[562]"><img class="aligncenter size-medium wp-image-673" title="Datei als JPG speichern" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/als_jpg_speichern-300x260.jpg" alt="" width="300" height="260" /></a></p>
<p>Bei den Speicheroptionen setzten Sie bitte die <em>Qualität </em>(<span style="color: #ff0000;">1</span>) auf <em>Hoch </em>und klicken anschließend auf <em>Speichern </em>.</p>
<div id="attachment_669" class="wp-caption aligncenter" style="width: 383px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/jpeg_options.jpg" rel="lightbox[562]"><img class="size-full wp-image-669" title="jpeg_options" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/jpeg_options.jpg" alt="" width="373" height="300" /></a><p class="wp-caption-text">Qualtität für Bild festlegen</p></div>
<p><strong>Schritt 08: Thumbnail erstellen</strong></p>
<p>Wählen Sie im Menü <em>Bild -&gt; Bildgröße</em>. Für die Breite tragen Sie <em>400</em> unter <em>Pixel </em>ein und klicken anschließend auf <em>OK</em>.</p>
<div id="attachment_587" class="wp-caption aligncenter" style="width: 451px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/bildgroesse-aendern.jpg" rel="lightbox[562]"><img class="size-full wp-image-587" title="bildgroesse aendern" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/bildgroesse-aendern.jpg" alt="" width="441" height="364" /></a><p class="wp-caption-text">Bildgröße ändern</p></div>
<p><strong>Schritt 09: Thumbnail speichern</strong></p>
<p>Speichern Sie das verkleinerte Bild nun über Datei -&gt; Speichern unter  in dem  Ordner<em> roae_tutorial/fertige_bilder/thumbnail.jpg</em>. Wählen Sie als <strong>Dateityp auch hier wieder JPG</strong>. Als Qualitätsstufe dürfte hier Mittel in vielen Fällen ausreichend sein.</p>
<p><strong>Schritt 10: Fenster schließen</strong></p>
<p>Schließen Sie nun das Fenster mit dem Orignal-Bild. Eine Nachfrage ob Sie die Änderung speichern wollen verneinen Sie. Die Datei mit dem Wasserzeichen / Logo lassen Sie geöffnet.</p>
<p><strong>Schritt 11: Aufnahme beenden</strong></p>
<p>Beenden Sie nun die Aufnahme über den Button <em>Aufnahme beenden.</em></p>
<div id="attachment_589" class="wp-caption aligncenter" style="width: 287px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aufnamhe-beenden.jpg" rel="lightbox[562]"><img class="size-full wp-image-589" title="aufnahme beenden" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/aufnamhe-beenden.jpg" alt="" width="277" height="41" /></a><p class="wp-caption-text">Beenden der Aufnahme</p></div>
<h3>Aktion auf alle Bilder in einem Ordner anwenden / Stapelverarbeitung</h3>
<p>Damit die Aktion erfolgreich ausgeführt werden kann,<strong> schließen Sie alle Dateien in Photoshop</strong>. Anschließend öffnen Sie das Wasserzeichen (ROAE-Tutorial/logo.gif).</p>
<p><strong>Einstellungen</strong></p>
<p>Wählen Sie <em>Datei -&gt; Automatisieren -&gt; Stapelverarbeitung</em>. Es öffnet sich ein großer Dialog der sich in die Bestandteile Abspielen, Quelle, Ziel und Fehler unterteilt. Übernehmen Sie nun die Einstellungen wie im Screenshot gezeigt. Die einzelnen Werte sind gefolgt nochmal schriftlich erläutert:</p>
<div id="attachment_674" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/stapelverarbeitung_optionen.jpg" rel="lightbox[562]"><img class="size-medium wp-image-674" title="stapelverarbeitung_optionen" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/stapelverarbeitung_optionen-300x210.jpg" alt="" width="300" height="210" /></a><p class="wp-caption-text">Einstellungen für die Stapelverarbeitung</p></div>
<p><span style="text-decoration: underline;">Abspielen</span></p>
<p>Wählen Sie bei Satz <em>Standardaktion </em>(<span style="color: #ff0000;">1</span>), bei Aktion <em>Wasserzeichen und Thumbnails</em> (<span style="color: #ff0000;">2</span>).</p>
<p><span style="text-decoration: underline;">Quelle</span></p>
<p>Bei der Art der Qelle wählen Sie <em>Ordner </em>(<span style="color: #ff0000;">3</span>) damit Sie im Dialog den Pfad zu den Orginal-Bildern (ROAE-Tutorial/original-Bilder) angegeben können (<span style="color: #ff0000;">4</span>). <em>Öffnen in Aktionen überschreiben</em> (<span style="color: #ff0000;">5</span>) muss darüber hinaus aktiviert werden.</p>
<p><span style="text-decoration: underline;">Ziel</span></p>
<p>Bei Ziel wählen Sie wie bei<em> </em>Quelle  <em>Ordner </em>(<span style="color: #ff0000;">6</span>) und legen als Pfad im Dialog den Ordner fertige Bilder (ROAE-Tutorial/fertige-Bilder) fest (<span style="color: #ff0000;">7</span>). Hier muss <em>&#8220;Speichern unter&#8221; in Aktionen überschreiben</em> aktiviert werden (<span style="color: #ff0000;">8</span>), da sonst die Dateien überschrieben werden.</p>
<p>Im Bereiech <em>Dateibennenung </em>legen Sie bitte folgendes fest: <em>Dokumentenname </em>(<span style="color: #ff0000;">9</span>) + <em>_</em> (<span style="color: #ff0000;">10</span>) + Dreistellige Seriennummer (<span style="color: #ff0000;">11</span>) + <em>Erweiterung </em>(<span style="color: #ff0000;">12</span>). Die Anfangsseriennummer sollte den Wert <em>1</em> haben (<span style="color: #ff0000;">13</span>)</p>
<p><span style="text-decoration: underline;">Fehler</span></p>
<p>Im Bereich <em>Fehler </em>wählen Sie <em>Bei Fehlern anhalten</em> (<span style="color: #ff0000;">14</span>)</p>
<p><strong>Starten und Resultat</strong></p>
<p>Nachdem Sie all diese Einstellungen vorgenommen haben, klicken Sie auf OK (<span style="color: #ff0000;">15</span>). Kurz darauf können Sie Photoshop bei der Arbeit zuschauen, wie innerhalb weniger Sekunden die Bilder erstellt  und im Ordner fertige Bilder abgelegt werden:</p>
<div id="attachment_676" class="wp-caption aligncenter" style="width: 347px"><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/fertige_bilder1.jpg" rel="lightbox[562]"><img class="size-full wp-image-676" title="fertige_bilder" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/fertige_bilder1.jpg" alt="" width="337" height="197" /></a><p class="wp-caption-text">Die fertigen Bilder, automatisch generiert von Photoshop</p></div>
<p>Sie können nun natürlich die das Logo oder die Orginal-Bilder durch ihre eigenen ersetzen. Auch können Sie die Aktion anpassen und z.B. die Vorschaubilder mit abgerundeten Ecken versehen.</p>
<p>Für Fragen, Hinweise und Anmerkungen kann wie immer die Kommentarfunktion benutzt werden.</p>
<p><strong>Siehe auch</strong></p>
<ul>
<li><a href="http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/">Thumbnails erzeugen und Durchschnittsfarbe in PHP ermitteln</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/04/automatisieren-stapelverarbeitung-mit-adobe-photoshop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wordpress multidomainfähig machen &#8211; Eine Wordpress-Installation für mehrere Blogs</title>
		<link>http://blog.root-of-all-evil.com/2010/03/wordpress-multidomainfahig-machen-eine-wordpress-installation-fur-mehrere-blogs/</link>
		<comments>http://blog.root-of-all-evil.com/2010/03/wordpress-multidomainfahig-machen-eine-wordpress-installation-fur-mehrere-blogs/#comments</comments>
		<pubDate>Sun, 28 Mar 2010 14:23:58 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Erklärungen]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Mehrere Blogs]]></category>
		<category><![CDATA[Multidomain]]></category>
		<category><![CDATA[Multiple Blogs]]></category>
		<category><![CDATA[Umzug]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=666</guid>
		<description><![CDATA[<br />
<b>Warning</b>:  call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'Array' was given in <b>/WWWROOT/158237/htdocs/wp-includes/plugin.php</b> on line <b>166</b><br />
]]></description>
			<content:encoded><![CDATA[<p>Die Überlegung ist simpel: Ich besitze mehrere Blogs zu unterschiedlichen Themen, die ich auf demgleichen Server mit Verwendung der Wordpress-Blogsoftware und Verwendung der gleichen Datenbank hoste. Jeder Blog basiert auf seiner eigenen Wordpress-Installation und ist in seinem eigenen Ordner hinterlegt. Das bedeutet aber auch: Jeden Blog einzeln auf neue Wordpress-Installationen und für jeden Blog jedes einzelne Plugin bei Updates aktualisieren. Eine Menge Administrationsaufwand&#8230;</p>
<p>Dabei geht es viel einfacher. Warum nicht eine einzelne Wordpress-Installation für mehrere Blogs und Domains nutzen? Die Wordpress-Installation also multidomainfähig machen. So muss nur ein einziges Wordpress-System muss aktualisiert werden und alle meine Blogs arbeiten mitder neuesten Version.</p>
<p><span id="more-666"></span></p>
<h4>1. Schritt &#8211; alle Wordpress-Installationen auf die gleiche Version updaten</h4>
<p>Damit die Zusammenführung reibungslos funktioniert müssen zunächst alle vorhandenen Wordpress-Installationen ein letztes mal auf die aktuellste &#8211; oder zumindest gleiche &#8211; Version aktualisiert werden.</p>
<h4>2. Schritt &#8211; einen neue Wordpress-Installation in neuem Verzeichnis anlegen</h4>
<p>Sind alle Wordpress-Installationen auf die gleiche Version aktualisiert worden müssen wir eine komplett neue Wordpress-Installation anlegen. Dazu <a title="Wordpress herunterladen" href="http://wordpress.org/download/" target="_blank">laden wir die benötigte Wordpress-Version herunter</a> und entpacken sie auf unseren Server in einem neuen Verzeichnis. Achtung: Die Installation selbst muss nicht ausgeführt werden. Lediglich die Dateien müssen auf den Server entpackt werden.</p>
<h4>3. Schritt &#8211; eine neue Datenbank anlegen (optional)</h4>
<p>Um die neue Installation von allen vorherigen zu trennen empfehle ich eine neue Datenbank anzulegen. Das muss aber nicht sein. Die Wordpress-Installation kann auch in einer bereits vorhanden Datenbank arbeiten.</p>
<h4>3. Schritt &#8211; die wp-config.php anpassen</h4>
<p>Der Trick ist einfach: Wordpress nutzt für die Datenbank einen Table_Prefix. Dieser wird in der wp-config.php festgelegt und kann von uns beliebig verändert werden. Folgende Änderungen an der Variablendeklaration von $table_prefix müssen an der wp-config vorgenommen werden:</p>
<pre class="php" title="code">$table_prefix = str_replace(array('.', '-' ), '_', $_SERVER['HTTP_HOST'] ) . '_';
$table_prefix = str_replace(array('www_'), '', $table_prefix);</pre>
<p>Unser Table_Prefix wird jetzt also gemäß unserer aufgerufenen Domain ausgewählt. Striche und Punkte werden in Unterstriche übersetzt, das &#8220;www&#8221; entfernt. Beispiel:</p>
<ul>
<li>&#8220;www.mein-cooler-blog.de&#8221; wird auf die Tabellen mit dem Table_Prefix &#8220;mein_cooler_blog_de_&#8221; basieren</li>
<li>&#8220;www.meinblog.com&#8221; wird auf die Tabellen mit dem Table_Prefix &#8220;meinblog_com_&#8221; basieren</li>
<li>&#8220;blog.meine-domain.de&#8221; wird auf die Tabellen mit dem Table_Prefix &#8220;blog_meine_domain_de_&#8221; basieren</li>
</ul>
<p>Dazu müssen die Verbindungsdaten der (neu erstellten) Datenbank wie gewohnt in der wp-config.php angepasst werden.</p>
<h4>4. Schritt &#8211; alle wp-content Ordner übertragen</h4>
<p>Sämtliche wp-content Ordner aus den alten Wordpress-Installationen müssen in die neue Installation übertragen werden. Dabei werden alle Bilder, Medien, Plugins und Skins mit übertragen.</p>
<h4>5. Schritt &#8211; die Datenbanktabellen umziehen</h4>
<p>In diesem Schritt müssen alle Datenbanktabellen der einzelnen Wordpress-Installationen in die (neu erstellte) Datenbank umgezogen werden. Das lässt sich mit php_MyAdmin recht leicht lösen (allerdings wird der Table_Prefix nicht geändert und muss manuel geändert werden):</p>
<p><a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/phpMyAdminUmzug.png" rel="lightbox[666]"><img class="aligncenter size-medium wp-image-667" title="phpMyAdminUmzug" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/phpMyAdminUmzug-300x222.png" alt="Umzug einer Datenbank in phpMyAdmin" width="300" height="222" /></a>Das PHP-Skript macht dagegen alles vollautomatisch und ändert den Table_Prefix gleich mit:</p>
<pre class="php" title="code">&lt;?php
function echoOk($msg) { echo('[OK]    ' . $msg . "\n"); }
?&gt;&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;title&gt;Datenbank-Umzug&lt;/title&gt;
&lt;style&gt;
    div.form label, div.form input {
        display: block;
    }
    div.form label {
        margin: 10px 0 5px 0;
    }
    div.form input {
        margin: 5px 0 10px 0;
    }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;?php
    $formElements = array('server', 'username', 'password',
        'database', 'newdatabase',
        'tableprefix', 'newtableprefix');
    $showForm = false;
    foreach($formElements as $element) {
        if(!isset($_POST[$element])) {
            $showForm = true;
        }
    }
    if($showForm) {
?&gt;
&lt;div class="form"&gt;
    &lt;form action="" method="post"&gt;
        &lt;fieldset&gt;
            &lt;label for="server"&gt;Datenbank-Server&lt;/label&gt;
            &lt;input type="text" name="server" /&gt;
            &lt;label for="username"&gt;Benutzername&lt;/label&gt;
            &lt;input type="text" name="username" /&gt;
            &lt;label for="password"&gt;Passwort&lt;/label&gt;
            &lt;input type="password" name="password" /&gt;
        &lt;/fieldset&gt;
        &lt;fieldset&gt;
            &lt;label for="database"&gt;Ursprüngliche Datenbank&lt;/label&gt;
            &lt;input type="text" name="database" /&gt;
            &lt;label for="newdatabase"&gt;Neue Datenbank&lt;/label&gt;
            &lt;input type="text" name="newdatabase" /&gt;
            &lt;label for="tableprefix"&gt;Ursprünglicher Table_Prefix&lt;/label&gt;
            &lt;input type="text" name="tableprefix" /&gt;
            &lt;label for="newtableprefix"&gt;Neuer Table_Prefix&lt;/label&gt;
            &lt;input type="text" name="newtableprefix" /&gt;
        &lt;/fieldset&gt;
        &lt;fieldset&gt;
            &lt;p&gt;ACHTUNG: Dieses Skript sendet manche der eingegebenen Daten ungefiltert an die Datenbank und überschreibt bereits existierende Tabellen beim Umzug!&lt;/p&gt;
            &lt;input type="submit" /&gt;
        &lt;/fieldset&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;?php } else {
    echo('&lt;pre&gt;');
    try {

        $con = mysql_connect($_POST['server'], $_POST['username'], $_POST['password']);
        if(!$con) {
            throw new Exception('Verbindung mit Server nicht möglich. ' . mysql_error());
        } else {
            echoOk('Mit \'' . htmlentities($_POST['server']) . '\' verbunden.');
        }

        if(!mysql_select_db($_POST['database'], $con)) {
            throw new Exception('Auswahl der Datenbank nicht möglich. ' . mysql_error());
        } else {
            echoOk('Aktuelle Datenbank \'' . htmlentities($_POST['database']) . '\'.');
        }

        $res = mysql_query('SHOW TABLES IN `' . $_POST['database'] . '` LIKE \'' . $_POST['tableprefix'] . '%\'', $con);
        if(!$res) {
            throw new Exception('Fehler bei Datenbankabfrage. ' . mysql_error());
        } else {
            while($result = mysql_fetch_row($res)) {
                $tables[] = $result[0];
                $newTables[] = str_replace($_POST['tableprefix'], $_POST['newtableprefix'], $result[0]);
            }
            $numTables = count($tables);
            if($numTables &gt; 0) {
                echoOk($numTables . ' Tabellen mit Table_Prefix \'' . htmlentities($_POST['tableprefix']) . '\' in Datenbank \'' . htmlentities($_POST['database']) . '\' gefunden.');
            } else {
                throw new Exception('Keine Tabellen mit Table_Prefix \'' . htmlentities($_POST['tableprefix']) . '\' gefunden.');
            }
        }

        foreach($tables as $num =&gt; $table) {
            $res = mysql_query('DROP TABLE IF EXISTS `' . $_POST['newdatabase'] . '`.`' . $newTables[$num] . '`', $con);
            if(!$res) {
                throw new Exception('Fehler bei Datenbankabfrage: ' . mysql_error());
            }
            $res = mysql_query('CREATE TABLE `' . $_POST['newdatabase'] . '`.`' . $newTables[$num] . '` LIKE `' . $_POST['database'] . '`.`' . $table . '`');
            if(!$res) {
                throw new Exception('Fehler bei Datenbankabfrage: ' . mysql_error());
            }
            $res = mysql_query('INSERT INTO `' . $_POST['database'] . '`.`' . $table . '` SELECT * FROM `' . $_POST['newdatabase'] . '`.`' . $newTables[$num] . '`');
            if(!$res) {
                throw new Exception('Fehler bei Datenbankabfrage: ' . mysql_error());
            } else {
                $process = round($num / $numTables * 100);
                if($process &lt; 10) {
                    $process = ' ' . $process;
                }
                if($process &lt; 100) {
                    $process = ' ' . $process;
                }
                echoOk('[' . $process . '%] Tabelle \'' . htmlentities($table) . '\' nach \'' . htmlentities($newTables[$num]) . '\' kopiert.');
            }
        }
        echoOk('[100%] ' . $numTables . ' Tabellen kopiert.');

        if(!mysql_close($con)) {
            throw new Exception('Verbindung mit Server konnte nicht geschlossen werden.');
        } else {
            echoOk('Verbindung mit Server geschlossen.');
        }

    } catch(Exception $e) {
        echo('[ERROR] ' . $e-&gt;getMessage() . "\n");
    }
    echo('&lt;/pre&gt;');
} ?&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Das Skript zum schnellen Umzug von mysql Tabellen kann auch hier heruntergeladen werden: <a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/dbUmzug.zip">PHP Skript zum schnellen Umzug von mysql Tabellen</a></p>
<h4>6. Schritt &#8211; Domain ändern</h4>
<p>Letzter Schritt: Die Domain muss jetzt nur noch auf den Speicherplatz der neuen Wordpress-Installation umgestellt werden.</p>
<h4>Troubleshooting bei &#8220;kaputten&#8221; Umlauten</h4>
<p>Sollten Umlaute nicht funktionieren, empfehle ich diesen Blogpost: <a href="http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/" target="_blank">Den Inhalt einer mysql Datenbank in utf8 umwandeln</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/03/wordpress-multidomainfahig-machen-eine-wordpress-installation-fur-mehrere-blogs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
