Mein Pagerank
So langsam scheint Google zu merken, dass mein Blog wichtig ist! đ
Am 17.09. war ich noch auf Platz 149 zu finden, wenn man nach „php blog“ sucht.
26.09. Platz 95
29.09. Platz 52.
06.10. Platz 46
02.11. Platz 48
Ich bin mir noch nicht ganz sicher, warum das gerade stagniert.
Aber es gibt einen Grund zu feiern: Mein Pagerank ist am vergangenen Wochenende von 0 auf 2 gestiegen! Yeah!
Mein gesetztes Ziel sind die Top 10 beim Suchbegriff „php blog“. Ich nehme Tipps immer gern entgegen, wie ich das schaffen kann. Es gibt nĂ€mlich viele andere gute PHP-Blogs, und an denen vorbeizuziehen wird nicht einfach. AuĂerdem sind einige Verzeichnisdienst- und Katalog-EintrĂ€ge vor meiner eigentlichen Website, was ich gern auch noch beheben wĂŒrde. Mal sehen.
Der beste Tipp ist wahrscheinlich, mehr gute Artikel zu schreiben. Daran arbeite ich *Schweiss von der Stirn wisch*.
Hier gibt es ĂŒbrigens Informationen ĂŒber den Google Pagerank und wann genau die Updates stattfinden. Ich bin ja sehr gespannt ob der Pagerank Ende des Jahres abgeschafft wird, die Diskussion lĂ€uft ja gerade (mal wieder).
Asynchron Aufgaben erledigen (lassen)
Was könnte der Autor damit meinen, werdet ihr euch fragen. Naja, das ist schnell erklÀrt: hier und da hat man mal kleine Aufgaben, die evtl. etwas lÀnger dauern, von dessen Ergebnis der User aber nicht wissen muss. Warum also soll man damit seine PHP-Scripte ausbremsen, und den User warten lassen? Ihn interessiert das Ergebnis ja (erstmal) nicht.
Beispiel: Nehmen wir an, das Versenden eine Informations-Email wĂŒrde 1 Sekunde dauern (wenn man ĂŒber einen externen SMTP versendet und nicht ĂŒber den lokalen, kann das sogar sein). Dann bemerkt der User auf der Webseite eine Verzögerung, die Seite wird langsam.
Es gibt sicherlich noch bessere Beispiele, wie z.B. das Berechnen eines Vorschaubildes. Der User lĂ€dt ein Bild hoch (Avatar), und wir brauchen davon eine verkleinerte Version. Wenn dieser Algorithmus nun langsam wĂ€re (was er im Normalfall nicht ist), wĂŒrde der User vor seinem Bildschirm DĂ€umchen drehen.
FĂŒr all solche Aufgaben (ihr findet sicherlich bessere) ist es sinnvoller, diese Aufgaben asynchron erledigen zu lassen. Da gibt es mindestens 2 Möglichkeiten:
- Man trÀgt die Aufgabe in eine Task-Tabelle in der Datenbank ein, und lÀsst ein weiteres Script (Cronjob?) diese Task-Liste abarbeiten.
- Man lÀsst einen Kindprozess diese Arbeit erledigen, welcher geforkt wird, und non-blocking ist
In beiden FÀllen bekommt der User direkt die nÀchste Seite zu sehen und kann weiterarbeiten, der asynchrone Prozess kann aber erst einige Sekunden/Minuten spÀter fertig sein.
Wie setzt man das nun um? Ich glaube, die erste Möglichkeit sollte jedem von euch bekannt sein, da es keine Schwierigkeit ist, eine neue Zeile in einer Datenbank-Tabelle einzufĂŒgen, und auch ein Script, das periodisch diese Zeilen liest und abarbeitet, sollte kein Hexenwerk sein.
Die zweite Möglichkeit haben wahrscheinlich deutlich weniger Leute genutzt. Ich finde sie auch ehrlich gesagt unschöner, aber möchte sie trotzdem vorstellen (da sie schneller umzusetzen ist, und hĂ€ufig auch zum Ziel fĂŒhrt). Als erstes fĂ€llt einem da vielleicht der system() Aufruf ein, mit dem man einen weiteren Prozess starten kann. Das Problem daran ist, dass er blocking ist, sprich der nĂ€chste Befehl in PHP erst ausgefĂŒhrt wird nachdem der System-Aufruf beendet ist. Genau das wollen wir ja nicht. Aber unter jedem Betriebssystem gibt es da Möglichkeiten, den Prozess in den Hintergrund zu schieben. Der system() Aufruf ist dann sofort beendet und PHP lĂ€uft weiter, parallel lĂ€uft ein weiterer Prozess mit der Aufgabe.
function execInBackground($cmd) { if (substr(php_uname(), 0, 7) == "Windows") { pclose(popen("start /B ". $cmd, "r")); } else { system($cmd . " > /dev/null &"); } }
In Maugrim The Reaper’s Blog gibt es eine sehr ausfĂŒhrliche Serie zu dem Thema, mit vielen weiteren Beispielen und Code. Auf jeden Fall lesenswert.
Eine andere interessante Möglichkeit ist Gearman.
http://toys.lerdorf.com/archives/51-Playing-with-Gearman.html
Wenn man etwas andere Voraussetzung hat, könnten die curl_multi_* Funktionen interessant sein. Diese kann man nutzen, wenn man viele Dinge zu erledigen hat, aber auf die Ergebnisse warten muĂ (weil man sie dem User anzeigen muss). Man möchte also nicht asynchron, sondern simultan arbeiten. Das ganze ist in einem Blogartikel zum Thema „Simultaneuos HTTP requests in PHP with cURL„ super beschrieben.
Javascript Web Worker
ErgĂ€nzend zu einem Ă€lteren Artikel ĂŒber Google Gears hier eine kurze Statusmeldung: Mittlerweile sind die modernen Browser (zur Zeit Firefox 3.5, Safari 4 und Google Chrome) in der Lage, die FunktionalitĂ€t der Worker auch ohne das Gears-Plugin anzubieten, sodass man auf eine groĂe Anzahl an Nutzern zurĂŒckgreifen kann. Wir werden also in Zukunft vermehrt tolle Seiten mit vielen Effekten und FunktionalitĂ€ten sehen, und vielleicht ja auch selbst entwickeln.
Weitere Infos bietet die Suchmaschine eurer Wahl. Einfach nach „Javascript Web Worker“ suchen, es gibt bereits einige Beispiele und auch den Draft, der diese Technik hoffentlich bald zum Standard macht.
Konstanten, Klassenkonstanten und der Zugriff
Variablen kennt jeder. Konstante werden deutlich seltener genutzt, haben aber auch ihre Daseinsberechtigung, vor allem bei Werten, die wir einmalig am Anfang setzen wollen und im spÀteren Verlauf auf keinen Fall geÀndert werden sollen.
Normale Konstanten in PHP werden angelegt mit
define('SERVICE_PORT', 312);
Darauf zugegriffen wird einfach mit dem Konstantennamen:
echo SERVICE_PORT;
In der objektorientierten Welt grauselt es einem, wenn man Konstanten einfach so in der Welt rumfliegen hat. Konstanten gehören meistens zu einer Klasse, und sollten demnach nicht im globalen Namensraum definiert werden.
Das kann man wie folgt machen:
class My_User { const STATUS_DELETED = -1; const STATUS_REGISTERED = 0; const STATUS_ENABLED = 1; private $_status; public function __construct() { $this->_status = self::STATUS_ENABLED; } }
Innerhalb der Klasse kann man also mit self und dem zweifachen Doppelpunkt darauf zugreifen. Von auĂerhalb der Klasse funktioniert das genauso:
echo My_User::STATUS_DELETED;
Etwas trickreicher ist es, wenn man den Konstantennamen in einer Variablen gespeichert hat und dann darauf zugreifen möchte. Hier mein erster Versuch, der natĂŒrlich nicht funktioniert hat:
$status = 'STATUS_REGISTERED'; echo My_User::$status;
In diesem Fall versuchen wir nicht, die Klassenkostante STATUS_REGISTERED auszugeben, sondern die statische Klassenvariable $status. Die Fehlermeldung lautet aussagekrÀftig:
Fatal error: Access to undeclared static property: My_User::$status in constant.php on line 17
Auch andere Versuche wie z.B.
echo My_User::'STATUS_REGISTERED'; echo My_User::{'STATUS_REGISTERED'};
scheitern.
Des RÀtsels Lösung ist die PHP-Funktion constant(). Damit kann man nicht nur normale Konstanten abrufen, sondern auch Klassenkonstanten:
echo constant('SERVICE_PORT'); echo constant('My_User::' . $status);
———–
Es gibt ĂŒbrigens noch eine weitere Methode, die man aber tunlichst NICHT verwenden sollte:
eval('echo My_User::' . $status . ';');
PHP beschleunigen mit dem APC
Frameworks im Allgemeinen sind eine sehr tolle Sache. Sie bieten eine Menge Klassen und FunktionalitĂ€ten, um einem die Arbeit zu erleichtern. Viele Teile eines Frameworks kann man auch einzeln nutzen, und diese sind hĂ€ufig auch recht schlank gehalten. Kernkomponenten jedoch, wie in diesem Beispiel das MVC-Konzept des Zend Frameworks, neigen dazu, recht viele Funktionen mitzubringen, die man evtl. garnicht alle benötigt. Das hat zur Folge, dass viele Dateien geladen werden, fĂŒr die der Webserver immer auf die Festplatte zurĂŒckgreifen muss. Das selbe gilt natĂŒrlich auch fĂŒr groĂe Projekte, die kein Framework einsetzen, aber sehr viele oder groĂe Scripte verwenden und gut besucht sind. Und wir alle wissen, dass Festplatten noch immer (auch SSDs) eine der langsamsten Komponenten in einem Rechner sind.
Aber es gibt Abhilfe: Caching! Als erstes macht es natĂŒrlich viel Sinn, im PHP-Code selbst Daten und Objekte zu cachen. Immer wiederkehrende, sich selten Ă€ndernde Datenbank-Ergebnisse zum Beispiel. Diese kann man entweder in der Session speichern, oder in memcached-Instanzen, oder oder. Ăber Zend_Cache und die verschiedenen Backends hatte ich ja auch bereits geschrieben.
Zweitens macht es aber auch sehr viel Sinn, die PHP-Scripte selbst zu cachen. Erstens liegen sie auf der langsamen Festplatte, und zweitens liegen sie dort ja als Textdateien. Wenn PHP aufgefordert wird, sie auszufĂŒhren, erstellt es erstmal aus dieser Textdatei einen Bytecode. Dieser Bytecode ist maschinennah, und ist dann ausfĂŒhrbar. NatĂŒrlich kostet dieses Ăbersetzen Zeit. Im Normalfall wird bei jedem Aufruf das Script neu ĂŒbersetzt.
Bytecode-Caches, wie APC (Advanced PHP Cache) einer ist, speichern diesen Bytecode zwischen, nĂ€mlich im schnellen Arbeitsspeicher. Statt also bei jedem Request 50 Zend-Framework-Klassen zu ĂŒbersetzen, werden sie einfach aus dem Bytecode-Cache genommen, das bringt Speed!
Der APC soll in (ferner?) Zukunft direkt mit PHP ausgeliefert werden. APC ist aktuell eine PHP-Extension, die man einfach herunterlÀdt, in der php.ini aktiviert, und dann bekommt man schnellere Webseiten, man muss nicht eine Zeile PHP-Code Àndern. Was kann es schöneres geben?
Hier die Installation unter Ubuntu:
sudo apt-get install php-apc
fertig! Wenn man nun eine phpinfo Seite aufruft, ist dort ein Abschnitt ĂŒber APC enthalten.
Man kann diese Parameter natĂŒrlich alle Ă€ndern, vor allem die Cache-GröĂe sollte man anpassen, damit auch alles reinpasst. Es gibt auĂerdem ein kleines apc-Admin-Script, mit dem man den aktuellen Stand des Caches anzeigen lassen kann (das ist im Fall von Ubuntu unter /usr/share/doc/php-apc/apc.php.gz zu finden, oder aber im Quell-Archiv der PECL). Dieses php-Script einfach an ein sicheres Verzeichnis des Webservers kopieren, und man erhĂ€lt beim Betrachten:
Um die Illusion zu zerstören: Man kann die AusfĂŒhrungszeit mit dem APC nicht um 99% reduzieren. Aber die 2-5 fache Performance kann man durchaus rausholen, und das mit nur 5 Minuten Arbeit!
Ăbrigens kann man APC nicht nur zum Cachen des Bytecodes verwenden, nach der Installation kann man auch Objekte, und Daten etc innerhalb der PHP-Scripte im Arbeitsspeicher cachen. Dazu gibt es Funktionen wie apc_store(), apc_fetch usw. Mehr dazu im PHP-Manual.
Neben APC gibt es natĂŒrlich auch noch andere Bytecode-Caches. Darunter sind XCache, eAccelerator und Zend Platform/Zend Server.