PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Gewinnspiel: Zend Framework Poster

with 4 comments

Ich habe noch ein Zend Framework Poster von mayflower zu verschenken. In der Firma habe ich bereits eins an die Wand gepinnt, und jetzt ist mir noch ein zweites in die Hand gefallen.

zf_poster

Wenn ihr es kostenlos zugeschickt haben wollt, müsst ihr nur eine der folgenden Bedingungen erfüllen:

  • Einen schönen Gastartikel hier im Blog verfassen (bei mir via Email melden, wenn ihr das machen wollt  mail). Diese Möglichkeit verdoppelt die Gewinnchance!
  • Ihr schreibt in eurem Blog ein paar Zeilen über den Artikel meines Blogs, den ihr am interessantesten gefunden habt
  • Ihr schreibt in die Kommentare, wieviele iPhone-Apps aktuell im Repository-Browser von Appstarz sind (Wer nicht weiß, was Appstarz ist, kann danach googlen)
  • Ihr schreibt ein paar nette Zeichen über meinen Blog in Twitter. Ihr müsst dafür mindestens 50 Follower haben.

Falls euch noch etwas anderes einfällt, womit ihr mich überzeugen könnt, packt es in die Kommentare.

Hinterlasst bitte euren Namen hier in den Kommentaren, damit ihr an der Verlosung teilnehmt und ich euch kontaktieren kann. Die Verlosung wird am 11.10.2009 stattfinden. Ihr habt also 2 Wochen Zeit. Es wird wieder mein Zufallsgenerator der letzten Verlosung zum Einsatz kommen.

Viel Glück!

Written by Michael Kliewe

September 29th, 2009 at 8:59 am

Posted in Allgemein,PHP

Meine Lieblings-Session auf der PHP-Unconference 2009

with 2 comments

Die Unconference war viel zu kurz, die Sessions waren zu 95% extrem interessant und spannend. Mindestens genauso spannend waren die Gespräche zwischendurch. Es gibt im Wiki der Unconf zu jeder Session eine Zusammenfassung, trotzdem möchte ich hier versuchen, die wichtigsten Inhalte meines favorisierten Vortrags nochmal nachzuarbeiten. Die Session trug erst den Titel „PHP Performance Optimierung“, doch die 4 Vortragenden haben sich dazu entschlossen, ihn kurzerhand umzubenennen in „PHP Performance Pessimierung“. Denn aus Fehlern lernt man am besten.

Die Liste der Speaker lässt Großes erwarten: Johann-Peter Hartmann, Kris Köhntopp, Lars Jankowfsky und Stefan Priebsch

Die einführenden Worte begannen mit einer kurzen Zusammenfassung. Frameworks und vor allem das ganze „drumherum“ helfen uns, ein Webprojekt langsam zu machen, es ist nicht nur PHP selbst. Und man kann sehr vieles tun, damit doch wieder eine unerträgliche Performance zu Tage kommt und der Benutzer keine Lust mehr auf die Webseite hat.

Was bedeutet Performance? Die Stichworte „Latenz“ (ideal sind 29 Sekunden, weil nach 30 Sekunden der Browser in ein Timeout läuft), „Durchsatz“ (wie schaffe ich es, dass möglichst nur 1 Benutzer gleichzeitig die Webseite nutzen kann), und Funktion (wenn nach einem Klick auf einen Knopf nichts passiert) wurden genannt. Daran müssen wir nun arbeiten.

Damit wir etwas zu pessimieren haben, wurde eine häufig auftretende Architektur aufgemalt, wo wir ansetzen können. Diese sieht so aus:

pessi1

Wir beginnen im Browser. Javascript und vor allem AJAX ist ein prima Werkzeug, um eine Seite unbenutzbar zu machen, und möglichst viele Ressourcen auf dem Server zu verbrauchen. Ideal sind viele AJAX-Requests, möglichst viele Informationen durch Javascript verarbeiten („quatsch, wer braucht schon ein LIMIT im SQL-Server, Javascript kann auch nur die ersten 10 Ergebnisse anzeigen und den Rest verwerfen“). Dabei laden wir möglichst viele Dinge, die wir dann nicht anzeigen.

Im Loadbalancer wollen wir natürlich immer Level 7 inspecten, damit wir möglichst viel zu verarbeiten haben und es möglichst lang dauert, eine Entscheidung zu finden. Wir bauen viel Logik darein, damit wir idealerweise jeden Request auf den am meisten belasteten Server zuteilen.

Der Webserver speichert idealerweise die Sessions in einer Datenbank, damit wir schon für sowas ordentlich viel Traffic und Performance auf die Datenbank-Server verlagern. Die Dateien liegen auf einem NFS, damit bei jedem stat auch ein Netzzugriff stattfindet. Auf keinen Fall darf dort Memcache verwendet werden, wir cachen nicht im Speicher sondern auf der Festplatte. Durch möglichst komplexe Logik, gleichzeitigen Cache-Wipe, kompliziertes (Dead)-Locking, Abhängigkeiten und nicht eindeutige Cache-Keys bekommen wir ideale Voraussetzungen, um durch Caching noch langsamer als vorher zu arbeiten. Wunderbar!

Wenn dann PHP selbst drankommt, dürfen wir auf keinen Fall einen Byte-Code-Cache nutzen. PHP muss bei jedem Request die Scripte neu kompilieren, es könnte ja sein, dass sich die Dateien von einen auf den anderen Request geändert haben! Möglichst viele include_once auf relative Pfade helfen uns, viele Festplatten-Zugriffe (file-exists usw) zu generieren. Einige Frameworks unterstützen uns dabei auch ideal. Falls wir einen Autoloader verwenden, sollte das am häufigsten verwendete Verzeichnis auch als letztes durchsucht werden, so generieren wir für jeden Loadversuch extra Zugriffsversuche! Natürlich dürfen wir auch nicht vergessen, den Virenscanner zu aktivieren. PHP-Scripte sind hoch gefährlich und müssen bei jedem Zugriff gecheckt werden!

Mit version_compare und ini_set werden wir auch bei jedem Script die grundlegenden Einstellungen prüfen und neu setzen. Die php.ini könnte sich ja auch geändert haben.

Auch wenn wir sie anfangs nicht brauchen, stellen wir natürlich zu jeder Datenbank eine Verbindung her, wir könnten sie ja später brauchen, und dann haben wir sie schon da. Das selbe gilt für große Klassen, die wir evtl. später brauchen könnten. Auch ist die Datenbank ideal, um Bilder und anderen statischen Content zu speichern. Diese Daten holen wir dann mit PHP und liefern sie an den Browser. Statischen Content direkt ausliefern können wir nicht tun, wir müssen doch jeden Zugriff mittels PHP zählen, und das Cachen im Browser verhindertn, die Bilder könnten sich ja jederzeit ändern.

Wir brauchen zur Anzeige natürlich auch eine effektive, sehr funktionsreiche Template-Engine. SMARTY bietet sich da an, da haben wir ähnlich wie in PHP die if-Abfragen, Schleifen und Ausgaben. Perfekt, das brauchen wir, und PHP kann das nicht!

ORM ist auch gerade in der Mode, und wir wollen ja nicht unsere SQL-Statements selbst schreiben. Manuell Optimieren ist out, ORM bieten die Möglichkeit, den Query bei jedem Request neu zu generieren. Außerdem sind wir damit unabhängig von der Datenbank, denn wir wechseln sie ja ab und zu, deshalb lieber abstrakt bleiben und das von komplexen Frameworks erledigen lassen. Stored Procedures lassen wir auch weg, denn wir wollen ja die Datenbank entlasten und darauf nichts speichern.

pessi2

—–

Es war auf jeden Fall der lustigste Vortrag auf der Unconf, die Speaker und die Hörer hatten viel Spass bei der „Optimierung“ und auch ein sehr schöner Einstieg und Überblick für weitere Sessions.

Stichworte und Bilder findet ihr im Wiki-Eintrag zur Session. Hier nochmal ein Dankeschön an die Speaker, ihr seid spitze!

Written by Michael Kliewe

September 16th, 2009 at 2:54 am

GeoLocation mit modernen Browsern

with 8 comments

Der ein oder andere von euch hat vielleicht schonmal mit GeoLocations experimentiert. Es gibt freie GeoDatenbanken und GeoIP-Datenbanken, mit denen man eine IP-Adresse umwandeln kann in einen Ort (bzw. Region/Land).

Als ich nun vor einigen Monaten hörte, dass der Firefox bald eine neue GeoLocation-Funktionalität erhält, womit dann viele neue standortabhängige Dienste möglich sind, war ich gespannt was tolles neues dabei rauskommt. Zuerst dachte ich, dass man im Browser seine Daten eingibt, und dann sendet das der Browser an die Webseite, falls die das wissen möchte.

Doch ich habe die Rechnung ohne die ganzen mobilen Geräte gemacht, für die das Feature primär interessant ist (ich selbst habe zwar einen PDA, aber keinen Internet-Daten-Tarif, deshalb hab ich daran nicht wirklich gedacht).

Was kann das Feature nun? Es passiert folgendes: Falls die Webseite die GeoLocation ermitteln möchte, bekommt der Benutzer eine Frage präsentiert:

geolocation1

Man kann dann entscheiden, ob man diese Informationen senden möchte oder nicht. Falls man „Ja“ klickt, wird die IP-Adresse an einen Google-Service geschickt. Außerdem werden noch GPS-Informationen und erreichbare WLAN-Netze (incl. ermittelter Empfangsstärke) versendet an Google Location Services. Daraus kann Google dann eine mehr oder weniger genaue Position ermitteln (bei GPS natürlich sehr genau bis auf wenige Meter, bei WLAN-Informationen kann es auch schon sehr ungenau werden, je nachdem wieviele WLANs da sind und ob diese bereits kartographiert wurden).

Diese Standort-Information erhält dann der Browser, und dann kann diese Information mittels Javascript API abgefragt werden. Das hier sind die Informationen, die man erhält:

geolocation3

Man erhält also die Angaben in Form von Latitude und Longitude, sowie evtl. weitere GPS-Informationen wie Höhe, Geschwindigkeit usw. Falls man diese Informationen in einen Ortsnamen umwandeln möchte, benötigt man entweder wieder eine Datenbank mit Informationen oder man nutzt einen Webservice, der das selbe tut.
In meiner Demo nutze ich letzteres, und lasse anhand der Latitude/Longitude den Namen der Stadt ermitteln, dazu nutze ich www.geonames.org.

Inklusive Stadt sieht das Endergebnis dann so aus:

geolocation5

Wie sieht das nun im Quelltext aus? Eigentlich sehr übersichtlich:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>

    <title>PHP Gangsta Geolocation</title>
    <SCRIPT TYPE="text/javascript" SRC="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></SCRIPT>

    <script type="text/javascript">
        function startgeolocation() {
            // Check for geoLocation Support
            if (navigator.geolocation) {
                // the location is looked up only once
                navigator.geolocation.getCurrentPosition(renderPosition, renderError);
                // every time the location changes
                // navigator.geolocation.watchPosition(renderPosition, renderError);
            } else {
            	dojo.byId('geoResults').innerHTML = '<p>Your browser does not support geolocation.</p>';
            }
        }

        // I use Dojo to render the output and do an AJAX call to get city name
        function renderPosition(position) {
            if (!window.count) window.count = 0;

            count ++;
            // var urlJSON = 'http://ws.geonames.org/findNearbyPlaceNameJSON?lat=-36.9104718&lng=174.9133483';
            var urlJSON = 'geonames.php?lat='+position.coords.latitude+'&lon='+position.coords.longitude;

            dojo.byId('d').innerHTML = '<div id="results" style="width: 200px; height: 200px; text-align: left"><p>'
                            + 'Latitude: ' + position.coords.latitude + '<br />'
                            + 'Longitude: ' + position.coords.longitude + '<br />'
                            + 'Accuracy: ' + position.coords.accuracy + '<br />'
                            + 'Update number: ' + count + '<br />'
                            + 'Altitude: ' + position.coords.altitude + '<br />'
                            + 'Altitude accuracy: ' + position.coords.altitudeAccuracy + '<br />'
                            + 'Heading: ' + position.coords.heading + '<br />'
                            + 'Speed: ' + position.coords.speed + '<br />'
                            + 'Google Maps: <a href="http://maps.google.com/maps?geocode=&q=' +
                            			position.coords.latitude + '+' + position.coords.longitude +
                            			'">Click</a><br />'
                            + '</p></div>';

            // now get the XML reverse geo data
           dojo.xhrGet( { //
		        // The following URL must match that used to test the server.
		        url: urlJSON,
		        handleAs: "json",
		        timeout: 5000, // Time in milliseconds
		        // The LOAD function will be called on a successful response.
		        load: function(responseObject, ioArgs) {
		        	for each (var item in responseObject.geonames) {
		        		dojo.byId('jsonResults').innerHTML = '<p>You live in: ' + item.name + '</p>';
		        	}
		          	return responseObject;
		        },
		        // The ERROR function will be called in an error case.
		        error: function(responseObject, ioArgs) { //
		          	console.error("HTTP status code: ", ioArgs.xhr.status); //
		          	return responseObject;
	          	}
		    });
        }

        function renderError() {
        	dojo.byId('geoResults').innerHTML = '<p>The page could not get your location.</p>';
        }
    </script>
</head>

<body onload="startgeolocation();">

	<div align="center">
	<h1>GeoLocation Demo</h1>
	<div id="geoResults">
	    <div id="d"><img src="loadingAnimation.gif" /></div>
	    <div id="jsonResults">city information are fetched...</div>
	</div>

</body>
</html>

Das einzig verwunderliche ist vielleicht der AJAX-Aufruf. Ich hatte ja geschrieben, dass ich den Namen der Stadt von geonames.org holen möchte. Da die Cross-Domain-Policy nur einen Ajax-Aufruf zum selben Server erlaubt, brauchen wir ein kleines „Proxy-Script“, welches so aussieht:

<?php
echo file_get_contents('http://ws.geonames.org/findNearbyPlaceNameJSON?lat='.$_GET['lat'].'&lng='.$_GET['lon']);
?>

Damit umgehen wir die Cross-Domain-Policy und erhalten unser Ergebnis in JSON-Form. Das war auch schon der ganze Zauber.

Aktuell beherrschen meines Wissens nach der Firefox 3.5, Chrome und die aktuellen Beta-Versionen von Safari und Opera diese Funktionalität. Google Gears bietet eine ähnliche Funktionalität (browserübergreifend), aber ich kennen niemanden, der Gears installiert hat.
Man kann übrigens im Firefox den Location Service ändern, falls man Google nicht mag:
geolocation4

Falls ihr ein Handy mit Internet habt, könnt ihr auch mal Google Maps Mobile ausprobieren. Außerdem könnt ihr hier mein Demo-Script ausprobieren.

Ich bin gespannt, was für interessante Dienste da bald aus dem Boden sprießen. Ich meine schonmal etwas gehört zu haben von „gucken wo Freunde gerade sind“ und sowas. Ich bin auch gespannt, wann ich endlich ein neues Handy mit Datentarif bekomme…

Written by Michael Kliewe

September 10th, 2009 at 10:33 am

Posted in PHP

Tagged with , ,

PHP-Unconference 2009 am Wochenende

without comments

Die PHP-Unconference findet dieses Jahr wieder statt, und zwar vom 12.09. – 13.09.2009 (also nächstes Wochenende!) im Hamburger Geomatikum.

Da ich den Termin letztes Jahr verpasst habe, bin ich umso glücklicher, dieses Jahr dabei sein zu können. Da ich dort privat hinfahre, kommt mir der Preis von 25 Euro natürlich sehr entgegen. Andere (größere) PHP-Konferenzen kosten häufig pro Tag mehr als 500 Euro, und hier gibt es 2 Tage für 25 Euro. Cool!

Organisiert wird es von der PHP-User-Group Hamburg und der Informatik-Abteilung Uni Hamburg.

Falls sich jemand fragt, was eine Unconference ist: Es handelt sich hierbei nicht um eine klassische Konferenz mit fester Agenda und starren Vorträgen, sondern vielmehr um kleine Präsentationen, Diskussionen und Gesprächen rund um alle Themen. Jeder Teilnehmer kann auch selbst auf der Bühne stehen. Es bleibt aufgrund der Größe (ca. 200 Leute werden da sein) im kleineren Rahmen. In der ersten Session wird abgestimmt, welche Themen auf die Bühne kommen. Ich bin gespannt, ist meine erste Unconference!

Ich freue mich auch schon besonders auf mein Wunschthema „PHP im Enterprise. Skalierbarkeit und Sicherheit“, das ziemlich beliebt ist, denn aktuell ist es auf Platz 1 der Vorschlags-Liste. Auch 3 bekannte Persönlichkeiten/Firmen haben schon angeboten, Vorträge dazu zu halten. Hier könnt ihr schauen, welche Themen sonst noch interessant sind.

Ob ich wohl einen meiner Leser dort treffen werde? Ich lasse mich überraschen.

PS: Falls jemand von euch eine günstige Möglichkeit kennt, dort für <30 Euro zu übernachten (nein, Auto zählt nicht), möge er sich schnell melden!

Written by Michael Kliewe

September 9th, 2009 at 4:32 pm

CSS Sprites

with one comment

In einem meiner letzten Artikel zum Thema „ Externe Javascript Dateien zusammenfassen“ haben wir bereits gemerkt, dass man HTTP-Requests verringern sollte, um die Performance auf dem Client und die Benutzererfahrung zu verbessern. Dies kann im Falle von CSS-Dateien weniger effektiv sein als beispielsweise bei Javascript-Dateien.

Bei Bildern in großer Anzahl muss der Browser diese auch nacheinander laden, was zu Verzögerungen bei der Anzeige führen kann. Nehmen wir an, wir haben eine Webseite mit vielen Grafiken. Das Menu besteht aus 10 Bildern, jedes davon hat einen Mouseover-Effekt, und es gibt noch einige weitere Bilder. Es resultiert also in >25 Bildern, die der Browser einzeln laden muss. Außerdem stehen dann die Mouseover-Bilder bereits von Anfang an zur Verfügung, und müssen nicht erst geladen werden, wenn die Maus darüber bewegt wird, wir haben also eine Art eingebauten Preload.

Um die Anzahl von HTTP-Requests zu verringern, kann man alle Bilder in ein großes Bild packen, und mit Hilfe von CSS dann den richtigen Abschnitt anzeigen. In diesem Schritt kann man auch nochmal diese Bilder etwas komprimieren, um noch Bandbreite zu sparen, denn häufig sehen die Bilder auch mit weniger Farben genausogut aus.

Ein solches Bild könnte zB. so aussehen:

nav-final

Wir haben also viele kleine Bilder in einem Bild untergebracht. Um diese Bildchen dann nutzen zu können und nur einen kleinen Ausschnitt dieses großen Bildes anzeigen zu können, nutzen wir die CSS-Funktion „background-position“.

Beispielsweise könnte der HTML-Code so aussehen:

<ul id=”navigation”>
	<li id=”navigation-01"><a href=”#”><span>Blog</span></a></li>
	<li id=”navigation-02"><a href=”#”><span>Portfolio</span></a></li>
	<li id=”navigation-03"><a href=”#”><span>Resume</span></a></li>
	<li id=”navigation-04"><a href=”#”><span>Contact</span></a></li>
</ul>

Wir schreiben die einzelnen Menupunkte noch als Text rein, damit Suchmaschinen sie lesen können. Das entsprechende CSS sieht dann so aus:

#navigation {
	background:url(”/images/navigation.jpg”) no-repeat;
	width:490px;
	height:40px;
	margin:0;
	padding:0;
}

#navigation span {
	display: none;
}

#navigation li, #navigation a {
	height:40px;
	display:block;
}

#navigation li {
	float:left;
	list-style:none;
	display:inline;
}

#navigation-01 {width: 98px;}
#navigation-02 {width: 131px;}
#navigation-03 {width: 123px;}
#navigation-04 {width: 138px;}

#navigation-01 a:hover {background:url(”/images/navigation.jpg”) 0px -40px no-repeat; }
#navigation-02 a:hover {background:url(”/images/navigation.jpg”) -98px -40px no-repeat; }
#navigation-03 a:hover {background:url(”/images/navigation.jpg”) -229px -40px no-repeat; }
#navigation-04 a:hover {background:url(”/images/navigation.jpg”) -352px -40px no-repeat; }

Standardmäßig soll die „erste Zeile“ des Bildes angezeigt werden. Das erreichen wir, indem wir das ganze Bild nehmen, aber nur 490*40 Pixel anzeigen (Zeilen 2-6). Wir blenden die spans aus (Zeile 10), ändern noch das Verhalten der Liste, damit die einzelnen Punkte nicht untereinander, sondern nebeneinander erscheinend (Zeilen 19-21). Wir definieren die Breite der einzelnen Listenelemente, und spezifizieren zum Schluss noch den Hover-Effekt, der einen anderen Ausschnitt anzeigen soll.

Es gibt auch Online CSS Sprite Generatoren, mit deren Hilfe es einfach ist, viele einzelne Bilder zu einem großen Bild zusammenzufassen, und dann die entsprechenden Koordinaten zu erhalten. Schaut euch einfach die folgenden Webseiten an:

http://www.csssprites.com

http://spritegen.website-performance.org

Written by Michael Kliewe

September 3rd, 2009 at 1:09 am