<?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 &#187; PHP</title>
	<atom:link href="http://blog.root-of-all-evil.com/category/codeschnipsel/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.root-of-all-evil.com</link>
	<description>Studium, Codeing und Gedachtes</description>
	<lastBuildDate>Mon, 30 Jan 2012 22:58:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<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[<a href="http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/" title="Punkte in Google-Mail-Adressen"></a>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, so hat man automatisch auch folgende weitere Adressen das.ist.eine.emailadresse@googlemail.com oder das.istEineEmailAdresse@googlemail.com etc. &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/05/punkte-in-google-mail-adressen/" title="Punkte in Google-Mail-Adressen"></a><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>1</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[<a href="http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/" title="PHP und Geolocation - Lokalisierung leicht gemacht"></a>Ein Besuch auf www.regnets.in 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: www.regnets.in/Hamburg oder www.regnets.in/Berlin. Geolocation lässt &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/05/php-und-geolocation-lokalisierung-leicht-gemacht/" title="PHP und Geolocation - Lokalisierung leicht gemacht"></a><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>3</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[<a href="http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/" title="fsockopen statt file_get_contents für HTTP-Requests"></a>Bei zahlreichen Webspace-Providern ist die Funktionalität von file_get_contents für http deaktiviert. Es treten dann Fehlermeldungen wie Warning: file_get_contents() [function.file-get-contents]: URL file-access is disabled in the server configuration in [..] und Warning: file_get_contents([..]) [function.file-get-contents]: failed to open stream: no suitable wrapper &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/04/fsockopen-statt-file_get_contents-fur-http-requests/" title="fsockopen statt file_get_contents für HTTP-Requests"></a><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>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[<a href="http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/" title="Validierung von URLs mit Zend_Validate"></a>Dieser Beitrag beschreibt kurz wie man mit Zend_Validate prüfen kann, ob eine URL / URI syntaktisch korrekt und die verlinkte Ressource erreichbar ist. Aufbau einer URL prüfen Ein eigenständiger Validator ist im Zend Framework nicht vorhanden welcher den Aufbau einer &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/04/validierung-von-urls-mit-zend_validate/" title="Validierung von URLs mit Zend_Validate"></a><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>1</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[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.
<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/04/ebay-trading-api-codeschnipsel-teil-1/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/04/ebay-trading-api-codeschnipsel-teil-1/" title="eBay Trading API Codeschnipsel - Teil 1"></a><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>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[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...<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/03/wordpress-multidomainfahig-machen-eine-wordpress-installation-fur-mehrere-blogs/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/03/wordpress-multidomainfahig-machen-eine-wordpress-installation-fur-mehrere-blogs/" title="Wordpress multidomainfähig machen - Eine Wordpress-Installation für mehrere Blogs"></a><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>
		<item>
		<title>Den Inhalt einer mysql Datenbank in utf8 umwandeln</title>
		<link>http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/</link>
		<comments>http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 09:08:08 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[convert]]></category>
		<category><![CDATA[converter]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[skript]]></category>
		<category><![CDATA[umwandeln]]></category>
		<category><![CDATA[utf8]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=552</guid>
		<description><![CDATA[Es kann schon einiges an Kopfzerbrechen verursachen, wenn beispielsweise ein Wordpress-Update nur noch kryptische Zeichen statt Umlaute darstellt. Meist liegt das Problem am in der Datenbank verwendetem Zeichensatz. Daten, die beispielsweise in einem latin1 Zeichensatz formatiert sind und in einer utf8-Tabelle abgespeichert werden, werden dadurch beim Auslesen meist unbrauchbar.<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/" title="Den Inhalt einer mysql Datenbank in utf8 umwandeln"></a><p>Es kann schon einiges an Kopfzerbrechen verursachen, wenn beispielsweise ein WordPress-Update nur noch kryptische Zeichen statt Umlaute darstellt. Meist liegt das Problem am in der Datenbank verwendetem Zeichensatz. Daten, die beispielsweise in einem latin1 Zeichensatz formatiert sind und in einer utf8-Tabelle abgespeichert werden, werden dadurch beim Auslesen meist unbrauchbar.</p>
<p>Das PHP-Skript hier hilft bei der Umwandlung des <em>Inhalts </em>der Datenbanktabellen in die utf8 Zeichenkodierung. Das Skript wandelt zunächst alle Tabellendaten in Binärdaten um, ändert dann die Tabellencodierung und wandelt schließlich die Tabellendaten wieder in das ursprüngliche Format um. Dadurch werden zum Beispiel Umlaute in ihre utf8-Entsprechungen verwandelt.</p>
<p><span id="more-552"></span></p>
<h3>Das ist zu tun</h3>
<ol>
<li>Zunächst alle Tabellen im Originalformat belassen</li>
<li><strong>Eine SICHERHEITSKOPIE DER DATENBANK ANLEGEN</strong></li>
<li>Das Skript in einem Texteditor öffnen und die Datenbankparameter anpassen</li>
<li>Das Skript auf den Webspace hochladen und im Browser öffnen</li>
<li>Das Skript vom Webspace löschen</li>
</ol>
<h3>Wie funktioniert das Skript? Kann ich das auch ohne Skript machen?</h3>
<p>Der Trick ist ganz einfach: Wandel ich den Zeichenkodierung einer Tabellenspalte mit folgendem mysql-Befehl in Binärdaten um &#8230;</p>
<pre title="code" class="sql">ALTER TABLE Tabellenname CONVERT TO CHARACTER SET binary</pre>
<p>&#8230; änder dann mit diesem Befehl hier die Zeichenkodierung der Tabelle auf utf8 &#8230;</p>
<pre title="code" class="sql">ALTER TABLE Tabellenname CONVERT TO CHARACTER SET utf8</pre>
<p>&#8230; muss ich im nächsten Schritt die Tabellenspalte wieder auf den ursprünglichen Typ zurücksetzen:</p>
<pre title="code" class="sql">ALTER TABLE Tabellenname MODIFY Spaltentyp Spaltenoptionen</pre>
<p>(Wobei Spaltentyp der ursprüngliche Spaltentyp wie Text, Varchar, Enum etc. ist und die Spaltenoptionen die Optionen wie zB. NOT NULL oder DEFAULT sind.)</p>
<h3>Das Skript</h3>
<pre class="php" title="code">&lt;?php

$DB_HOST = 'localhost'; // Enter your Database Host
$DB_USER = 'username'; // Enter your Database Username
$DB_PASSWORD = 'password'; // Enter your Database Password
$DB_DATABASE = 'database'; // Enter your Database Name

$tables = array();
$tables_with_fields = array();

$link_id = mysql_connect($DB_HOST, $DB_USER, $DB_PASSWORD) or die('Error establishing a database connection');
echo 'Connected' ."\n";
mysql_select_db($DB_DATABASE, $link_id);
echo 'Selected database' ."\n";

// Gathering information about tables and all the text/string fields that can be affected
// during the conversion to UTF-8.
echo 'Getting tables:' ."\n";
$resource = mysql_query("SHOW TABLES", $link_id);
while ( $result = mysql_fetch_row($resource) ) {
	$tables[] = $result[0];
	echo ' - ' . $result[0] ."\n";
}

if ( !empty($tables) ) {
    echo 'Starting process' ."\n";
    foreach ( (array) $tables as $table ) {
        echo 'Working on table "' . $table . '"';
        $resource = mysql_query("EXPLAIN $table", $link_id);
        while ( $result = mysql_fetch_assoc($resource) ) {
            if ( preg_match('/(char)|(text)|(enum)|(set)/', $result['Type']) )
                $tables_with_fields[$table][$result['Field']] = $result['Type'] . " " . ( "YES" == $result['Null'] ? "" : "NOT " ) . "NULL " .  ( !is_null($result['Default']) ? "DEFAULT '". $result['Default'] ."'" : "" );
                echo '.';
        }
        echo "\n";
    }

    // Change all text/string fields of the tables to their corresponding binary text/string representations.
    echo 'Altering tables to binary character set';
    foreach ( (array) $tables as $table ) {
        mysql_query("ALTER TABLE $table CONVERT TO CHARACTER SET binary", $link_id);
        echo '.';
    }
    echo "\n";

    // Change database and tables to UTF-8 Character set.
    echo 'Altering tables to utf8 character set';
    mysql_query("ALTER DATABASE " . $DB_DATABASE . " CHARACTER SET utf8", $link_id);
    foreach ( (array) $tables as $table ) {
        mysql_query("ALTER TABLE $table CONVERT TO CHARACTER SET utf8", $link_id);
        echo '.';
    }
    echo "\n";

    // Return all binary text/string fields previously changed to their original representations.
    echo 'Altering binary text/string fields to original representation';
    foreach ( (array) $tables_with_fields as $table =&gt; $fields ) {
        foreach ( (array) $fields as $field_type =&gt; $field_options ) {
            mysql_query("ALTER TABLE $table MODIFY $field_type $field_options", $link_id);
        }
        echo '.';
    }
    echo "\n";

    // Optimize tables and finally close the mysql link.
    echo 'Optimizing tables' . "\n";
    foreach ( (array) $tables as $table )
        mysql_query("OPTIMIZE TABLE $table", $link_id);
    mysql_close($link_id);
    echo 'DONE';
} else {
    die('There are no tables?');
}
?&gt;</pre>
<p>Das Skript basiert auf dem mitlerweile nicht mehr weiter entwickeltem <a href="http://wordpress.org/extend/plugins/utf-8-database-converter/" target="_blank">WordPress UTF-8 Database Converter</a> und lässt sich in der obigen Form <a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/03/utf8-converter.zip">hier herunterladen</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/03/den-inhalt-einer-mysql-datenbank-in-utf8-umwandeln/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Entwickeln mit Zend &#8211; Teil 1, Einführung</title>
		<link>http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/</link>
		<comments>http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 14:27:59 +0000</pubDate>
		<dc:creator>Felix</dc:creator>
				<category><![CDATA[Guides]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projekt]]></category>
		<category><![CDATA[Framework]]></category>
		<category><![CDATA[Projekt mit Zend]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend Server]]></category>
		<category><![CDATA[Zend Studio]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=447</guid>
		<description><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/" title="Entwickeln mit Zend - Teil 1, Einführung"></a>In dieser Reihe von Publizierungen werden alle notwendigen Schritte beschrieben, eine professionelle Entwicklungsumgebung aufzusetzen mit welcher mit dem Zend Framework entwickelt werden kann. Als ZF-Projekt soll beispielhaft ein Gästebuch mit Administrationsbereich erstellt werden. Hierfür wird zuerst auf dem System (Windows &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/" title="Entwickeln mit Zend - Teil 1, Einführung"></a><p>In dieser Reihe von Publizierungen werden alle notwendigen Schritte beschrieben, eine professionelle Entwicklungsumgebung aufzusetzen mit welcher mit dem <a href="http://framework.zend.com/">Zend Framework</a> entwickelt werden kann. Als ZF-Projekt soll beispielhaft ein Gästebuch mit Administrationsbereich erstellt werden.</p>
<p>Hierfür wird zuerst auf dem System (Windows 7) Apache, MySQL und PHP installiert (Zend Community Server) und der Apache entsprechend konfiguriert, dass das zukünftige Projekt über Virtual Hosts ansprechbar ist. Um den Code komfortabel zu verwalten verwenden wir das Versionskontrollsystem Subversion welches wir gegen unbefugte Benutzung absichern werden. Anschließend wird eine Entwicklungsumgebung (Zend Studio für Eclipse) installiert, welche komfortabel mit der Versionsverwaltung und dem Server zusammenarbeiten kann.</p>
<p>Letzlich wird begonnen das eigentliche Projekt, die Programmierung des Gästebuchs, mit Hilfe von verschiedenen Pattern (z.B. MVC) umzusetzen.</p>
<h3><span id="more-447"></span>Was ist das Zend Framework</h3>
<p>Das Zend Framework (oft abgekürzt mit ZF) ist ein komponentenorientieres Framework für PHP 5, welches zahlreiche Klassen bereit stellt mit welchen sehr komfortabel Anwendungen entwickelt werden können.<br />
Komponentenorientiert in diesen Zusammenhang heißt, dass neue Anwendungen komplett auf dem Framework bassierend entwickelt werden können- alternativ können zu bestehenden Anwendungen aber auch nur bestimmte Funktionen, z.B. Cacheing, Authentifizierung, von dem Framework genutzt werden.<br />
Im März 2006 wurde die erste Pre-Alpha-Version 0.1.1 vorgestellt und wird seit dem stetig von Zend Technologies &#8211; den Entwicklern von PHP &#8211; weiterentwickelt.</p>
<h3>Warum das Zend Framework</h3>
<p>Frameworks sind mehrere dutzende verfügbar, eine Wahl fällt hier auf den ersten Blick sehr schwer. Stellt man jedoch eine Reihe von Kriterien auf, wird die Auswahl schnell auf wenige eingeschränkt. Ich möchte hier nur ein Paar der zahlreichen Vorteile listen, welche ich bei dem Zend Framework gegenüber anderen Frameworks sehe:</p>
<ul>
<li>Komponentenbasierend<br />
Wie bereits im Paragraphen &#8220;Was ist das Zend Framework&#8221; beschrieben. Um das ZF zu nutzen müssen bestehende Anwendungen nicht neu geschrieben werden, sondern es können bestimmte Funktionalitäten ohne großen Aufwand genutzt werden.<br />
Zahlreiche Standardaufgaben wie Formularerstellung und -validierung, E-Mailversandt oder Templates werden durch die Komponenten abgedeckt.<br />
Durch  durchgehend benutzte Design Patterns (&#8220;Entwicklungsmuster&#8221;) und Adapter (ein Art abkopelbarer Zwischenschritt) können sehr schnell durch wenige Zeilen Code große Teile der Anwendung umgeschrieben werden.  So ist es z.B. innerhalb weniger Sekunden möglich die Anwendung so umzuschreiben, dass diese zukünftig in einer ini-/ xml-Datei oder SQLite-DB statt in einer Oracle-/ MySQL-Datenbank Fehlermeldungen protokolliert.</li>
</ul>
<ul>
<li>Community<br />
Das ZF hat eine sehr große Community die von Zend selbst wiederum stark gefördert wird (siehe <a href="http://framework.zend.com/wiki/display/ZFDEV/Home">Zend Framework Wiki</a>). Es existieren zahlreiche, natürlich auch deutschsprachige, Foren, Blogs und Tutorials die sich mit dem ZF beschäftigen.</li>
</ul>
<ul>
<li>Dokumentation<br />
Das ZF ist vorbildhaft dokumentiert. Die Dokumentation wird von Zend selbst immer auf den neusten Stand gehalten, ist in zahlreichen Sprachen verfügbar und verfügt über zahlreiche Beispiele und Tutorials.</li>
</ul>
<ul>
<li>Web 2.0<br />
Es stehen zahlreiche Klassen bereit, mit welchen man die Services von Google, Delicous, YouTube, Flickr, Yahoo, Amazon etc. ohne hohe Einarbeitungszeit nutzen kann.</li>
</ul>
<ul>
<li>Ajax<br />
Das <a href="http://www.dojotoolkit.org/">Dojo Toolkit</a> ist in das ZF integriert. Hierdurch können sehr schnell Ajax-Applikationen erstellt werden.<br />
Auch hier gilt wieder &#8211; Dojo kann verwendet werden, muss aber nicht. Sind sie bereits ein anderes Framework gewohnt, können Sie dieses ebenfalls mit dem ZF nutzen.</li>
</ul>
<ul>
<li>Weiterentwicklung<br />
Da hinter dem ZF die Zend Technologies steht, ist eine in Zukunft Weiterentwicklung gesichert &#8211; ein großer Pluspunkt im Vergleich zu anderen Frameworks.</li>
</ul>
<ul>
<li>Lizenzierung/Kosten<br />
Das ZF steht unter der <a href="http://de.wikipedia.org/wiki/BSD-Lizenz">BSD-Lizenz.</a> Kosten entstehen also nicht.</li>
</ul>
<ul>
<li>Testbasis<br />
Alle Komponenten werden über Unit-Tests getestet. Auch ist es so dem Entwickler möglich, mit relativ wenig Aufwand Tests für seine Applikation zu schreiben.</li>
</ul>
<h3>Warum Zend-Software</h3>
<p>In der Einleitung war schon abzulesen, dass hier fast nur Software von Zend verwendet wird. Hintergrund für diese Wahl ist, dass ich persönlich von den Zend-Produkten überzeugt bin: sie zeichnen sich aus durch eine komfortable, leicht zu erlernende Bedienung und zahlreiche kleine Tools welche die tägliche Arbeit stark erleichtern.</p>
<p><strong>Vorteile</strong></p>
<p>So ist es z.B. beim Zend Server möglich über eine Web-GUI schnell die Konfiguration zu ändern (z.B. bestimmte PHP-Extensions oder Error-Logging zu (de)aktivieren) und weil alles &#8220;aus einer Hand&#8221; kommt wird beim ersten Start von Zend Studio (nur eines von zahlreichen weiteren Beispielen) automatisch der Zend Server gefunden und das Debugging konfiguriert. Natürlich stell Zend Studio auch zahlreiche Tools bereit welche das Arbeiten mit dem ZF noch mehr beschleunigen. Mit Zend Tools ist übrigens ein Server-debugging möglich, das heißt der Code kann auf dem Server selbst, unabhängig vom Code in der Entwicklungsumgebung inspiziert werden.<br />
Zend Studio setzt auf Eclipse auf &#8211; es können also die Plugins welche man bereits in Eclipse verwendet hat bzw. kennt, auch in Zend Studio verwenden.</p>
<p><strong>Nachteile</strong></p>
<p>Nach viel Lob und Tadel sollen natürlich auch die negativen Aspekte beleuchtet werden: Zend Studio (Eclipse)  benötigt relativ viel Arbeitsspeicher. Sind mehrere große Projekte parallel geöffnet kann dies den Rechner schon stark belasten (insbesondere wenn Tools wie der HTML-Validator aktiv sind).<br />
Zahlreiche Kinderkrankheiten wurden in Verison 7.1 behoben, welches ein Update auf diese Version obligatorisch ist.<br />
Eine offizielle 64-Bit Version für Linux ist noch nicht verfügbar &#8211; zwar kann man die 32-Bit Version installieren, dies läuft aber instabil.<br />
Die Entwicklungsumgebung mag hin und wieder ein wenig oversized wirken: man kann wirklich alles damit machen. Über SSH auf einen Server zugreifen und direkt Remote Dateien editieren oder direkt Datenbanken entwickeln &#8211; dies ist aber manchmal einfach zuviel des guten wenn man nur &#8220;ein paar Zeilen Code&#8221; schreiben will.<br />
Auch arbeiten manche Funktionen, z.B. die Codevervollständigung in Javascript, nicht fehlerfrei; andere Funktionen welche z.B. den Code auf (logische) Korrektheit prüfen arbeiten suboptimal, sodass nach einem Import eines Projektes mehrere 1000 angbliche Fehler gelistet werden &#8211; dabei steht aber z.B. der schließende Tag in einer includeten-Datei, was Zend natürlich nicht weiß.</p>
<p><strong>Kosten</strong></p>
<p>Der Zend Server ist in der sog. Community Edition (eingeschränkter Fuktionsumfang) kostenlos erhältich. Für die Entwicklung reichen die Funktionen jedoch voll und ganz aus. Zend Studio kostet mit einem 1-Jahres-Abo für Updates und Support 399€, dies ist jedoch verglichen mit Funktionsumfang ok. Eine Testversion für 30 Tage ist verfügbar.</p>
<h3>Ausblick</h3>
<p>Im nächsten Beitrag werden wir den Zend Server CE herunterladen und installieren. Es wird ein kurzer Überblick über die Funktionen gegeben. Anschließend werden wir noch zwei Virtual Hosts einrichten (live, development) über welche unsere zukünftige Anwendung dann erreichbar sein wird.</p>
<p><strong>Wichtige Links</strong></p>
<ul>
<li><a href="http://www.ralfeggert.de/">http://www.zend.com/de/</a> &#8211; Zend Technologies, Download von Zend Studio und Zend Server CE.</li>
<li><a href="http://framework.zend.com ">http://framework.zend.com </a>- Zend Framework, die offizielle Seite von ZF. Mit Links zur Dokumentation, Tutorials und dem ZF Wiki.</li>
<li><a href="http://www.dojotoolkit.org/">http://www.dojotoolkit.org/</a> &#8211; Dojo Toolkit, das in ZF integrierte Ajax-Toolkit</li>
<li><a href="http://www.zfforums.com/">http://www.zfforums.com/</a> &#8211; ZF Forum, englischsprachiges ZF Forum.</li>
<li><a href="http://www.ralfeggert.de/">http://www.ralfeggert.de/</a> &#8211; Blog von Ralf Eggert, blogt über das ZF und PHP allgemein.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/03/entwickeln-mit-zend-teil-1-einfuhrung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thumbnails erzeugen und Durchschnittsfarbe in PHP ermitteln</title>
		<link>http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/</link>
		<comments>http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 19:36:22 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Guides]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projekt]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Durchschnittsfarbe]]></category>
		<category><![CDATA[Imagecolorat]]></category>
		<category><![CDATA[Imagecopyresampled]]></category>
		<category><![CDATA[Thumbnails generieren]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=435</guid>
		<description><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/" title="Thumbnails erzeugen und Durchschnittsfarbe in PHP ermitteln"></a>Eines meiner etwas zurückliegenden Projekte zwang mich externe Bilder durch meinen Server zu laden, zu verkleinern und schließlich auch die Durchschnittsfarbe zu berechnen. Aus Gründen der Performancesteigerung war ich nie ein Fan davon die Durchschnittsfarbe Pixel für Pixel des Originalbildes &#8230;<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/" title="Thumbnails erzeugen und Durchschnittsfarbe in PHP ermitteln"></a><p>Eines meiner etwas <a title="qolors.net Visual Bookmarking" href="http://www.qolors.net" target="_blank">zurückliegenden Projekte</a> zwang mich externe Bilder durch meinen Server zu laden, zu verkleinern und schließlich auch die Durchschnittsfarbe zu berechnen. Aus Gründen der Performancesteigerung war ich nie ein Fan davon die Durchschnittsfarbe Pixel für Pixel des Originalbildes zu berechnen. <a href="http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln#durchschnittsfarbe" title="zum Berechnen der Durchschnittsfarbe springen">Es gibt eine viel elegantere und fast genauso exakte Lösung</a>:</p>
<p><span id="more-435"></span>Unser Ziel: Wir möchten ein Bild (jpg, gif, png) über einen HTTP-Stream laden,  dieses lokal auf dem Server speichern, ein entsprechendes Thumbnail  (eine verkleinerte Version) erzeugen, speichern und schlussendlich die  dominierende &#8211; oder auch durchschnittliche &#8211; Farbe im Bild berechnen und  abspeichern. Eventuell möchten wir zusätzlich alles in einer Datenbank  hinterlegen &#8211; das soll aber zunächst außerhalb unserer Betrachtung  liegen.</p>
<p>Für das Berechnen der Durchschnittsfarbe gibt es einen wunderbar  einfachen Trick. Doch dazu später mehr. Beginnen wir zunächst einmal mit  einer Konstante und einer Funktion, die wir später noch benötigen:</p>
<pre class="php" title="code">DEFINE('CTX', stream_context_create(array(
    'http' =&gt; array(
        'timeout' =&gt; 20,
        'method' =&gt; 'get',
        'header' =&gt; 'Referer: XYZ'
    )
)));</pre>
<p>Unsere Konstante CTX ist also ein array. Wir benötigen es für unsere  HTTP-Anfrage, mit der wir das gewünschte Bild auf unseren Server laden.  Für diese Anfrage verwenden wir die HTTP-GET-Methode, definieren einen  Timeout von maximal 20 Sekunden und senden einen Referer im Header, den  ich hier mal einfach nur &#8220;XYZ&#8221; genannt habe.</p>
<pre class="php" title="code">function randomstring_creator($len = 20) {
    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $string = '';
    mt_srand((double) microtime() * 1000000);
    for($i = 0; $i &lt; $len; $i++) {
        $string .= $chars{mt_rand(0, strlen($chars))};
    }
    return($string);
}</pre>
<p>Die Funktion randomstring_creator() generiert uns eine zufällige Zeichenfolge mit einer beliebigen Länge. Wir benötigen diese Funktion, da wir in diesem Beispiel die Bilder lokal unter einem zufälligem Namen speichern werden.</p>
<p>Weiter geht es mit unseren Klassen. Beginnen wir mit der Klasse <em>Image</em>:</p>
<pre class="php" title="code">class Image {
    var $img;
    var $source;
    var $local;
    var $dimX;
    var $dimY;
    var $avg_colour;
    function get($source, $quality = 90) {
// siehe unten
    }
    function calc_avg_colour() {
// siehe unten
    }
}</pre>
<p>Unsere Klasse besitzt also die Eigenschaften <em>source </em>(die Url,  von der das Bild stammt), <em>local </em>(den lokalen Speicherpfad), <em>dimX</em>,  <em>dimY </em>(die Abmessungen des Bildes in Pixel) und <em>avg_colour</em> (den farblichen Durchschnittswert als HEX-Code in der Form rrggbb). Die  Eigenschaft <em>img </em>ist eine Referenz auf das Image-Objekt, dass ich  aber später noch näher beleuchten werde.</p>
<p>Die beiden Funktionen <em>get()</em> und <em>calc_avg_colour()</em> der  Klasse sollten selbsterklärend sein. Mit der Funktion <em>get()</em>, die  gleichzeitig auch als Konstruktor dient, laden wir das Bild, dass unter <em>source </em>zu finden ist, auf unseren Server und speichern es in der  gewünschten JPEG-Qualität (90 als Standard) lokal.</p>
<p><em>calc_avg_colour()</em> berechnet &#8211; wie der Name schon sagt &#8211; die  Durchschnittsfarbe des in der Eigenschaft <em>img</em> gespeicherten  Bildes.</p>
<p>Wir wollen zusätzlich aber auch kleine Abbilder des Originalbildes  erstellen und erweitern dazu unsere <em>Image</em>-Klasse um die Klasse <em>Thumbnail</em>:</p>
<pre class="php" title="code">class Thumbnail extends Image {
    function create($parent, $X = 300, $Y = 300, $quality = 60) {
// siehe unten
    }
}</pre>
<p>Diese Erweiterung besitzt zunächst nur eine Funktion, mit der das  Thumbnail erstellt wird. Die übergebene Variable <em>parent </em>enthält  dabei eine bereits existierende <em>Image</em>-Klasse, die  herunterskaliert werden soll. Die maximale Breite und Höhe werden mit  den Variablen <em>X</em> und <em>Y</em> (standard bei 300 Pixel) übergeben.  Die JPEG-Qualität beträgt standardmäßig nur 60.</p>
<p>Das eigentliche Programm sieht dann nur noch so aus:</p>
<pre class="php" title="code">$imgurl = 'original.jpg';
// neues Objekt der Klasse Image erzeugen
$img = new Image;
// Bild aus der imgurl laden
if($img-&gt;get($imgurl)) {
    // ein Objekt der Klasse Thumbnail erzeugen
    $thumb = new Thumbnail;
    // und ein Thumbnail des oben definierten Objektes der Klasse Image generieren
    $thumb-&gt;create($img, 300, 300);
    // dann noch die Durschschnittsfarbe berechnen
    $thumb-&gt;calc_avg_colour();
    // und Daten ausgeben... fertig
    echo('Image: ' . $img-&gt;source . "\n");
    echo('Thumbnail: ' . $thumb-&gt;source . "\n");
    echo('Average colour: #' . $thumb-&gt;avg_colour . "\n");
} else {
    echo('Could not get the image.');
}</pre>
<p>So, dann sehen wir uns also mal unsere Funktionen der Klassen an.  Beginnen wir mit der Funktion <em>get()</em>:</p>
<pre class="php" title="code">function get($source, $quality = 90) {
    $this-&gt;source = $source;
    // wir laden das Bild von $source:
    if(($file = file_get_contents($this-&gt;source, false, CTX))) {
        // wenn erfolgreich, erzeugen wir mit den geladenen Daten
        // ein neues Bild und speichern dieses in der Eigenschaft img
        $this-&gt;img = imagecreatefromstring($file);
        // wir lesen die Höhe und Breite des Bildes aus
        $this-&gt;dimX = imagesx($this-&gt;img);
        $this-&gt;dimY = imagesy($this-&gt;img);
        // und speichern das Bild unter zufälligem Namen mit imagejpeg lokal im Ordner "raw"
        $this-&gt;local = 'raw/' . randomstring_creator() . '.jpg';
        imagejpeg($this-&gt;img, $this-&gt;local, $quality);
        // bei Erfolg true zurückgeben
        return(true);
    } else {
        // false, wenn das Bild nicht geladen werden konnte
        return(false);
    }
}</pre>
<p>Ein Thumbnail erzeugen wir mit dieser Funktion der Klasse <em>Thumbnail</em>:</p>
<pre class="php" title="code">function create($parent, $X = 300, $Y = 300, $quality = 60) {
    // zunächst die vorläufigen Abmessungen speichern
    $this-&gt;dimX = $X;
    $this-&gt;dimY = $Y;
    // die Abmessungen und deren Verhältnis des Originalbildes holen
    $origX = $parent-&gt;dimX;
    $origY = $parent-&gt;dimY;
    $ratio = $origX / $origY;
    // wenn das Originalbild breiter als hoch ist, kürzen wir die Höhe
    if($X / $Y &gt; $ratio) {
        $this-&gt;dimX = $Y * $ratio;
    } else {
    // wenn das Originalbild höher als breit ist, kürzen wir die Breite
        $this-&gt;dimY = $X / $ratio;
    }
    // hier erstellen wir ein neues Bild mit den oben berechneten Abmessungen
    $this-&gt;img = imagecreatetruecolor($this-&gt;dimX, $this-&gt;dimY);
    // und kopieren das Originalbild einfach in das eben erstellte kleinere, dabei wird es verkleinert
    imagecopyresampled($this-&gt;img, $parent-&gt;img, 0, 0, 0, 0, $this-&gt;dimX, $this-&gt;dimY, $origX, $origY);
    // jetzt speichern wir das neue Thumbnail noch unter zufälligem Namen im Ordner "thumb"
    $this-&gt;source = $parent-&gt;local;
    $this-&gt;local = 'thumb/' . randomstring_creator() . '.jpg';
    imagejpeg($this-&gt;img, $this-&gt;local, $quality);
}</pre>
<p>Das Herzstück: Wir wollen die  Durchschnittsfarbe ermitteln. Dazu könnten wir einfach alle Farbwerte  aller Pixel zusammen addieren und dann durch die Anzahl der Pixel  teilen. Bei einem Thumbnail von der Größe 300 x 300 Pixel, würden wir  aber eine Schleife mit 90.000 Durchläufen benötigen. Es gibt einen  besseren Weg &#8211; einen ganz logischen Trick.<br />
<a name="durchschnittsfarbe"></a><br />
<h3>Durchschnittsfarbe berechnen</h3>
<p>Wir haben oben beim Erstellen des Thumbnails die Funktion <em>imagecopyresampled()</em> verwendet. Was wenn wir unser Thumbnail einfach auf eine Größe von 1&#215;1  Pixel skalieren? Genau: Es bleibt ein Pixel mit der Durschnittsfarbe  übrig. Dessen Farbe auszulesen ist mit <em>imagecolorat()</em> dann nur  noch eine Kleinigkeit. Los gehts:</p>
<pre class="php" title="code">function calc_avg_colour() {
    // ein temporäres Bild mit den Abmessungen 1x1 Pixel erstellen
    $avrg = imagecreatetruecolor(1, 1);
    // Das Thumbnail einfach reinkopieren, dabei auf 1x1 Pixel skalieren
    imagecopyresampled($avrg, $this-&gt;img, 0, 0, 0, 0, 1, 1, $this-&gt;dimX, $this-&gt;dimY);
    // Die Farbe des verbleibenden Pixels auslesen
    $rgb = imagecolorat($avrg, 0, 0);
    // Das temporäre Bild wieder löschen
    imagedestroy($avrg);
    // Und die Farbe in HEX umwandeln
    $r = dechex($rgb &gt;&gt; 16);
    $g = dechex($rgb &gt;&gt; 8 &amp; 255);
    $b = dechex($rgb &amp; 255);
    if(strlen($r) &lt; 2) $r = 0 . $r;
    if(strlen($g) &lt; 2) $g = 0 . $g;
    if(strlen($b) &lt; 2) $b = 0 . $b;
    // tataaaa... da ist unsere Durchschnittsfarbe:
    $this-&gt;avg_colour = $r . $g . $b;
}</pre>
<p>Super. Das wäre es. Ende und aus.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/02/thumbnails-erzeugen-und-durchschnittsfarbe-in-php-ermitteln/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Das Zend Framework und die eBay trading API – Teil 4</title>
		<link>http://blog.root-of-all-evil.com/2010/02/das-zend-framework-und-die-ebay-trading-api-%e2%80%93-teil-4/</link>
		<comments>http://blog.root-of-all-evil.com/2010/02/das-zend-framework-und-die-ebay-trading-api-%e2%80%93-teil-4/#comments</comments>
		<pubDate>Thu, 11 Feb 2010 16:09:35 +0000</pubDate>
		<dc:creator>Philipp</dc:creator>
				<category><![CDATA[Codeschnipsel]]></category>
		<category><![CDATA[Guides]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Projekt]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[eBay]]></category>
		<category><![CDATA[Guide]]></category>
		<category><![CDATA[trading API]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://blog.root-of-all-evil.com/?p=370</guid>
		<description><![CDATA[Wir werden einen Prozess schaffen, der es dem Nutzer erlaubt den eBay User Consent Flow mit Hilfe unserer Webapplikation zu durchschreiten. Dieser Prozess soll möglichst unkompliziert, direkt und intuitiv bedienbar sein. Ich empfehle also die Verwendung von Ajax-Methoden um das User Interface und den darzustellenden Prozess zu visualisieren. Wie das Zend Framework mit Ajax Anfragen umgehen kann wird hier also auch Thema sein.<p class="read-more"><a href="http://blog.root-of-all-evil.com/2010/02/das-zend-framework-und-die-ebay-trading-api-%e2%80%93-teil-4/">Read more &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<a href="http://blog.root-of-all-evil.com/2010/02/das-zend-framework-und-die-ebay-trading-api-%e2%80%93-teil-4/" title="Das Zend Framework und die eBay trading API – Teil 4"></a><p>Die ersten drei Teile dieser Reihe hatten sich mit der theoretischen und praktischen Implementierung des User Consent Flow Prozesses, der für die Authentifizierung des Nutzers mit und der Verwendung der eBay Trading API notwendig ist, befasst. Im vierten und letzten Teil sollen die Implementierungen der letzten drei Teile (<a title="Zum ersten Teil dieses Guides" href="http://blog.root-of-all-evil.com/?p=131" target="_self">Teil 1</a>, <a title="Teil zwei dieser Reihe" href="http://blog.root-of-all-evil.com/?p=161" target="_self">Teil 2</a>, <a title="Der dritte Teil dieser Reihe" href="http://blog.root-of-all-evil.com/?p=205" target="_self">Teil 3</a>) dieser Reihe in die Controller und Views umgesetzt und so ein visueller Anmelde- und Authentifizierungsprozess geschaffen werden.</p>
<h3>Was wird Teil 4 dieses Guides zeigen?</h3>
<p>Wir werden einen Prozess schaffen, der es dem Nutzer erlaubt den eBay User Consent Flow mit Hilfe unserer Webapplikation zu durchschreiten. Dieser Prozess soll möglichst unkompliziert, direkt und intuitiv bedienbar sein. Ich empfehle also die Verwendung von Ajax-Methoden um das User Interface und den darzustellenden Prozess zu visualisieren. Wie das Zend Framework mit Ajax Anfragen umgehen kann wird hier also auch Thema sein.<span id="more-370"></span></p>
<h3>Warum Ajax?</h3>
<p>Wenn wir uns eben an den Ablauf des (<a title="Teil 1" href="http://blog.root-of-all-evil.com/?p=131" target="_blank">in Teil 1 ausführlich beschriebenen</a>) Consent Flow Prozesses erinnern: Der Benutzer muss selbst nur wenige Klicks und Eingaben machen &#8211; namentlich die Anmeldung auf der eBay Login-Seite zu der er weitergeleitet wird. Im Hintergrund muss unsere Applikation allerdings zwei Anfragen machen. (1) Eine SessionID von der eBay trading API anfragen und (2) nach der Rückkehr des Benutzers<a href="http://blog.root-of-all-evil.com/wp-content/uploads/2010/01/ebayConsentFlow.jpg" rel="lightbox[370]"><img class="alignright size-thumbnail wp-image-139" title="eBayUserConsentFlow" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/01/ebayConsentFlow-150x150.jpg" alt="Der eBay User Consent Flow" width="150" height="150" /></a> zur Applikation den mit der SessionID verknüpften Auth-Token anfragen.</p>
<p>Hier empfehle ich Ajax: Dem Benutzer soll visualisiert werden, dass sich hinter den Kulissen etwas tut und nicht lange auf den Seitenaufbau warten müssen (die API-Anfragen dauern ein paar wenige Sekunden). Wir werden also die Seite ausliefern, und von dieser eine Ajax-Anfrage starten, die die getSessionID und später die fetchToken Actionen anstoßen wird. Der Benutzer wird ein Lade-Grafik sehen und damit merken, dass sich etwas tut. Die Weiterleitung soll vollautomatisch erfolgen. Das Ergebnis sieht also keinerlei Actionen vom Benutzer auf unserer Applikation vor. Er muss lediglich warten bis er zu eBay weiter geleitet wird und sich dort anmelden kann.</p>
<p><img class="aligncenter size-full wp-image-374" title="consentFlowGUI" src="http://blog.root-of-all-evil.com/wp-content/uploads/2010/02/consentFlowGUI.jpg" alt="" width="500" height="220" /></p>
<h3>Die benötigten Actions</h3>
<p>Wie in Teil 2 bereits aufgestellt brauchen wir neben dem Ebayauth Controller folgende fünf Actions:</p>
<ul>
<li><em>index</em> (stößt den Consent Flow an)<em><br />
</em></li>
<li><em>step1</em> (ist Ziel eines Ajax-Requests &#8211; übernimmt getSessionID)</li>
<li><em>success </em>(bei erfolgreichem Login auf eBay wird der Benutzer hierhin  weitergeleitet)</li>
<li><em>step2 </em>(ist Ziel eines Ajax-Requests &#8211; übernimmt fetchToken)</li>
<li>und <em>denied</em> (bei fehlgeschlagenem oder abgebrochenen Login)<em><br />
</em></li>
</ul>
<p>und vier Views für den Controller <em>Ebayauth</em>:</p>
<ul>
<li><em>index.phtml</em></li>
<li><em>step1.ajax.phtml</em></li>
<li><em>success.phtml<br />
</em></li>
<li><em>step2.ajax.phtml</em></li>
<li>und <em>denied.phtml</em></li>
</ul>
<h3>Ajax Requests erkennen und entsprechend handeln</h3>
<p>Um die Unterscheidung zwischen &#8216;normalem&#8217; Request und einem Ajax-Request können wir einen Context-Switcher verwenden, den uns das Zend Framework zur Verfügung stellt. Dieser muss in der init()-Methode des Controllers initialisiert werden uns sieht in unserem Fall so aus:</p>
<pre class="php" title="code">$contextSwitch = $this-&gt;_helper-&gt;getHelper('AjaxContext');
$contextSwitch-&gt;addActionContext('step1', 'html') // wir geben html zurück
              -&gt;addActionContext('step2', 'html') // möglich wäre auch xml oder json
              -&gt;initContext();

// Das übrige Layout abschalten
if($this-&gt;_request-&gt;isXmlHttpRequest()) {
     $this-&gt;_helper-&gt;layout-&gt;disableLayout();
}</pre>
<p>Unsere zwei step Actions benötigen noch eine kleine Fallunterscheidung. Wir möchten sie nur ausführen lassen, wenn es sich tatsächlich um einen Ajax-Request handelt:</p>
<pre class="php" title="code">if(!$this-&gt;_request-&gt;isXmlHttpRequest()) {
    $this-&gt;_redirect('/ebayauth/index'); // zum Start des Auth-Prozesses umleiten
}</pre>
<h3>Der Controller Ebayauth</h3>
<p>Springen wir gleich in den Code:</p>
<pre class="php" title="code">public function indexAction() {}

public function step1Action()
{
    if(!$this-&gt;_request-&gt;isXmlHttpRequest()) {
        $this-&gt;_redirect('/ebayauth/index');
    }

    // die getSessionID API-Anfrage durchführen
    $tokenAdapter = new Ebay_Auth_ConsentFlow();
    $return = $tokenAdapter-&gt;getSessionID();

    // Wir müssen die SessionID in einer Datenbank oä. zwischenspeichern
    $modelAccount = new Model_Account();

    if($return != false) {
        // zwischenspeichern - bitte durch eigenen Implementierung ersetzen
        $modelAccount-&gt;writeEbaySessionId($this-&gt;accountID, $return['sessionId']);
        if(isset($return-&gt;ShortMessage)) {
            // bei Fehler ist ShortMessage teil der API Rückgabe
            $this-&gt;view-&gt;error = (string)$return-&gt;ShortMessage;
        } else {
            $this-&gt;view-&gt;redirectURL = $return['redirectUrl'];
        }
    } else {
        $this-&gt;view-&gt;error = 'Keine Verbindung zu eBay.';
    }
}

public function successAction() {}

public function step2Action()
{
    if(!$this-&gt;_request-&gt;isXmlHttpRequest()) {
        $this-&gt;_redirect('/ebayauth/index');
    }

    // die im step1 zwischengespeicherte SessionID aus der Datenbank holen
    $modelAccount = new Model_Account();
    $sessionId = $modelAccount-&gt;getEbaySessionId($this-&gt;accountID);

    // die fetchToken API Anfrage ausführen
    $tokenAdapter = new Ebay_Auth_ConsentFlow();
    $return = $tokenAdapter-&gt;getToken($sessionId);

    if($return != false) {
        // auch hier wieder eigenen Implementierung Verwenden
        $modelAccount-&gt;writeEbayTokenId($this-&gt;accountID, $return['token'], $return['expiration']);
        if(isset($return-&gt;ShortMessage)) {
            $this-&gt;view-&gt;error = (string)$return-&gt;ShortMessage;
        } else {
            // Hier festlegen wohin umgeleitet werden soll
            $this-&gt;view-&gt;redirectURL = '/'; // zurück zur Startseiten
            $this-&gt;view-&gt;expiration = $return['expiration'];
        }
    } else {
        $this-&gt;view-&gt;error = 'Keine Verbindung zu eBay';
    }
}

public function deniedAction() {}</pre>
<p>Die drei Actions index, success und denied benötigen keine Methoden im Controller. Der JavaScript, der den Ajax-Request anstößt coden wir einfach fest in die views ein.</p>
<h3>Die Views</h3>
<p>Das hier ist der index view (<em>index.phtml</em>). Wir binden ein JavaScript ein und rufen die Funktion <em>shootAjaxHtml()</em> auf.</p>
<pre class="php" title="code">&lt;div&gt;
    &lt;div id="loader" class="redirect"&gt;&lt;img src="/images/preloader.gif" /&gt; Weiterleitung an eBay wird vorbereitet ...
        &lt;script type="text/javascript"&gt;shootAjaxHtml('/ebay/step1', 'loader')&lt;/script&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<p>Der <em>step1.ajax.phtml</em> View:</p>
<pre class="php" title="code">&lt;?php if(isset($this-&gt;redirectURL)) { ?&gt;
&lt;p&gt;Automatische Weiterleitung zu eBay in wenigen Sekunden. &lt;a href="&lt;?php echo($this-&gt;redirectURL); ?&gt;" title="Weiterleitung"&gt;Oder hier klicken.&lt;/a&gt;&lt;/p&gt;
&lt;script type="text/javascript"&gt;window.location = '&lt;?php echo($this-&gt;redirectURL); ?&gt;';&lt;/script&gt;
&lt;?php } elseif(isset($this-&gt;error)) { ?&gt;
&lt;p class="error"&gt;&lt;strong&gt;Fehler:&lt;/strong&gt; &lt;?php echo $this-&gt;error; ?&gt;&lt;/p&gt;
&lt;?php } ?&gt;</pre>
<p><em>success.phtml</em>:</p>
<pre class="php" title="code">&lt;div&gt;
    &lt;div id="loader" class="redirect"&gt;&lt;img src="&lt;?php echo SKINPATH; ?&gt;/images/preloader.gif" /&gt; eBay Anmeldung wird abgeschlossen ...
        &lt;script type="text/javascript"&gt;shootAjaxHtml('/ebay/authgettoken', 'loader')&lt;/script&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<p>Und <em>step2.ajax.phtml</em>:</p>
<pre class="php" title="code">&lt;?php if(isset($this-&gt;redirectURL)) { ?&gt;
&lt;p class="info"&gt;Die Verknüpfung des Benutzerkontos mit eBay verliert am &lt;?php echo date('d.m.Y', strtotime($this-&gt;expiration)); ?&gt; um &lt;?php echo date('H:m', strtotime($this-&gt;expiration)); ?&gt; Uhr seine Gültigkeit.&lt;/p&gt;
&lt;p&gt;&lt;a href="/ebay" class="black" title="Zurück zur Startseite"&gt;Weiter&lt;/a&gt;&lt;/p&gt;
&lt;?php } elseif(isset($this-&gt;error)) { ?&gt;
&lt;p class="error"&gt;&lt;strong&gt;Fehler:&lt;/strong&gt; &lt;?php echo $this-&gt;error; ?&gt;&lt;/p&gt;
&lt;?php } ?&gt;</pre>
<p>Im denied View gebe ich nur eine Fehlermeldung wie &#8220;Anmeldung fehlgeschlagen&#8221;.</p>
<h3>Der JavaScript (mit mootools):</h3>
<pre class="javascript" title="code">function shootAjaxHtml(action, loaderid)
{
    var remote = new Request.HTML({
        url: action + "?format=html",
        onSuccess: function(html) {
            $(loaderid).getParent('div').empty().adopt(html);
        },
        onFailure: function(xhr) {
            $(loaderid).getParent('div').empty().adopt(new Element('p').set('text', 'Es ist ein Fehler aufgetreten: ' +xhr.status +' ' +xhr.statusText).addClass('error'));
        }}).get();
}</pre>
<p>Und damit sind wir auch schon fertig.</p>
<p>Ich hoffe die ersten drei Teile dieser Einführung in eine Mögliche Implementierung des Consent Flows der eBay trading API in eine Webapplikation mit dem Zend Framework waren gut verständlich und konnten einen guten Überblick über die benötigten Implementierungsschritte geben. Teil vier hat anhand von Codebeispielen gezeigt, wie man den Consent Flow möglichst benutzerfreundlich umsetzen kann.</p>
<p>Empfehlungen, Anmerkungen, Verbesserungsvorschläge? Dazu sind die Kommentare da!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.root-of-all-evil.com/2010/02/das-zend-framework-und-die-ebay-trading-api-%e2%80%93-teil-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

