PHP Blog von PHPGangsta

Archive for the ‘PHP’ Category

Expires-Header und Komprimierung aktivieren im Apache2

with 14 comments

Wie wir alle wissen kann man die größten Performanceverbesserungen auf der Browserseite erreichen. Ich habe bereits das Zusammenfassen von CSS- und Javascript-Dateien vorgestellt, ebenso wie das Zusammenfassen von Bildern in CSS-Sprites. Damit wird die Anzahl der Request schonmal reduziert. Aber es gibt natürlich noch mehr zu tun, beispielsweise die Requests noch weiter reduzieren durch gute Cache-Einstellungen, die wir an den Brower senden. Das werde ich hier vorstellen ebenso wie die Komprimierung mittels gzip/deflate der Dateien, die dann noch heruntergeladen werden müssen.

Cache-Einstellungen werden via HTTP-Header an den Browser mitgesendet. Zum Beispiel der Expires-Header, der angibt wie lange der Browser eine Datei cachen soll bevor er nach einer neuen Version fragt. Diese Cachedauer setzt man je nach Dateityp relativ hoch auf mehrere Tage.

Damit die unten stehenden Anweisungen vom Apache interpretiert werden können muss man das “Expires-Modul” aktivieren, häufig kann man das wie folgt erledigen:

a2enmod expires

Hier die entsprechenden Apache-Einstellungen, die man entweder global im Apache einstellen kann, oder im entsprechenden vhost der Domain oder aber in der .htaccess-Datei speziell für Ordner:

<IfModule mod_expires.c>
    # turn on the module for this directory
    ExpiresActive on
    # set default
    ExpiresDefault "access plus 24 hours"

    # cache common graphics
    ExpiresByType image/jpg "access plus 1 months"
    ExpiresByType image/gif "access plus 1 months"
    ExpiresByType image/jpeg "access plus 1 months"
    ExpiresByType image/png "access plus 1 months"

    # cache CSS
    ExpiresByType text/css "access plus 1 months"

    # cache other filetypes
    ExpiresByType text/javascript "access plus 1 months"
    ExpiresByType application/javascript "access plus 1 months"
    ExpiresByType application/x-shockwave-flash "access plus 1 months"
</IfModule>

Mit dem Firefox-Addon Firebug kann man sich das Ergebnis anschauen:

Wie man im obigen Bild sehen kann wurde diese Datei auch gzip-komprimiert an den Browser gesendet. Dies macht vor allem Sinn bei textbasierten Dateien wie HTML, CSS, Javascript usw. Komprimierung bei bereits komprimierten Bildern wie jpg, png oder gif macht keinen Sinn, diese werden wir nicht komprimieren.

Dazu benötigen wir im Apache2 das “Modul Deflate“, und wir werden für bestimmte Dateitypen die Komprimierung aktivieren. Genau wie oben kann man die unten stehenden Anweisungen nach der Aktivierung des Moduls an die 3 bekannten Stellen setzen:

a2enmod deflate
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/xml
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE text/javascript
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/x-javascript
    AddOutputFilterByType DEFLATE application/x-shockwave-flash

    DeflateFilterNote Input input_info
    DeflateFilterNote Output output_info
    DeflateFilterNote Ratio ratio_info
    LogFormat '"%r" %s %{output_info}n/%{input_info}n (%{ratio_info}n%%)' deflate
    CustomLog /var/log/apache2/deflate_log deflate
</IfModule>

Die oberen Zeilen sollten verständlich sein, die unteren 5 Zeilen sind dazu da ein spezielles Log mit Komprimierungsinformationen anzulegen. Dieses sieht dann wie folgt aus:

"GET /wp-includes/images/smilies/icon_wink.gif HTTP/1.1" 200 -/- (-%)
"GET /wp-content/themes/journalist/images/top.gif HTTP/1.1" 200 -/- (-%)
"GET /wp-content/plugins/syntaxhighlighter/syntaxhighlighter/styles/shCore.css?ver=2.1.364b HTTP/1.1" 200 1917/7297 (26%)
"GET /wp-content/plugins/syntaxhighlighter/syntaxhighlighter/styles/shThemeDefault.css?ver=2.1.364b HTTP/1.1" 200 1095/3636 (30%)
"GET /wp-content/plugins/wp-cumulus/tagcloud.swf?r=5611069 HTTP/1.1" 200 34520/34610 (99%)
"GET /wp-content/plugins/wp-cumulus/tagcloud.swf?r=8457739 HTTP/1.1" 200 34520/34610 (99%)
"GET /wp-includes/js/tinymce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif HTTP/1.1" 200 -/- (-%)
"GET /wp-includes/js/tinymce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif HTTP/1.1" 200 -/- (-%)
"GET /wp-includes/js/tinymce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif HTTP/1.1" 200 -/- (-%)
"GET /wp-includes/js/tinymce/themes/advanced/link.htm?ver=327-1235-syntaxhighlighter2.3.8 HTTP/1.1" 200 974/2655 (36%)
"GET /wp-includes/js/tinymce/tiny_mce_popup.js?ver=327-1235 HTTP/1.1" 200 1921/5223 (36%)
"GET /wp-includes/js/tinymce/utils/mctabs.js?ver=327-1235 HTTP/1.1" 200 751/1786 (42%)
"GET /wp-includes/js/tinymce/utils/form_utils.js?ver=327-1235 HTTP/1.1" 200 1895/5303 (35%)
"GET /wp-includes/js/tinymce/utils/validate.js?ver=327-1235 HTTP/1.1" 200 1578/4764 (33%)
"GET /wp-includes/js/tinymce/themes/advanced/js/link.js?ver=327-1235 HTTP/1.1" 200 1467/4803 (30%)
"GET /wp-includes/js/tinymce/themes/advanced/skins/wp_theme/dialog.css?ver=327-1235-syntaxhighlighter2.3.8 HTTP/1.1" 200 1722/5510 (31%)
"GET /wp-includes/js/tinymce/themes/advanced/skins/wp_theme/img/fade-butt.png HTTP/1.1" 200 -/- (-%)
"GET /wp-includes/js/tinymce/themes/advanced/skins/wp_theme/img/tabs.gif HTTP/1.1" 200 -/- (-%)

Wie man sieht sind die Bilder nicht komprimiert worden, die CSS- und Javascript-Dateien sind teilweise auf 26% komprimiert worden. Wie man auch gut sehen kann bringt die Komprimierung der swf-Datei garnichts, man kann es also wieder deaktivieren.

Bei Änderungen an der apache2.conf bzw. den vhost-Dateien sollte man natürlich daran denken den Apache neuzustarten, damit die Änderungen aktiv werden.

In diesem Zusammenhang möchte ich auch nochmal auf die beiden sehr nützlichen Firefox-Addons Google Pagespeed und YSlow aufmerksam machen. Beide geben einen sehr guten Überblick wo Probleme liegen und was man noch verbessern kann.

Written by Michael Kliewe

März 8th, 2010 at 8:51 am

Buchvorstellung: PHP Design Patterns

with 8 comments

Vor einigen Wochen habe ich mir ein Buch zugelegt welches ich schon lange lesen wollte: PHP Design Patterns von Stephan Schmidt aus dem O’Reilly Verlag. Interessant ist vor allem die vor mir liegende 2. Auflage, die nochmal umfassend aktualisiert wurde und nun voll auf PHP 5.3 abgestimmt ist.

Auf den ersten Seiten wird auf die Objektorientierung von PHP und die neuen Features von PHP 5.3 eingegangen, unter anderem Namespaces, Lambda-Funktionen, Closures, Late Static Binding und Interzeptor-Methoden. Alle Beispiele im Buch beinhalten auch Namespace-Anweisungen. Wer sich dort sehr fit fühlt kann das Kapitel überspringen, aber eine solch gute Zusammenfassung sollte man eigentlich zu mindestens schnell überfliegen.

Die nächsten 2 Kapitel befassen sich mit der SPL und zeitgemäßem Software-Design: Die Iterator-Interfaces sind das ganze Buch über sehr wichtig, da immer gegen Interfaces implementiert wird, was ich so auch noch in keinem Buch gesehen habe, wirklich vorbildlich finde ich. Es werden auch Themen wie Fluent Interfaces, Dependency-Injection sowie UML angerissen, alles Dinge die man heutzutage kennen sollte.

Der Kern des Buches befasst sich natürlich mit den verschiedenen Entwurfsmustern: Erzeugungsmuster, Strukturmuster, Verhaltensmuster sowie Enterprise-Pattern. In letzterem werden Dinge wie MVC, die verschiedenen Datenbank-Pattern (Row-Data-Gateway, Active Record, Data Mapper usw), sowie Front-Controller, Event-Dispatcher, TemplateView, View-Helper und viele weitere behandelt.

Zu jedem Pattern gibt es eine kurze Zusammenfassung, die Motivation und den Zweck des Einsatzes (wo brauche ich das Pattern und welche Probleme löst es für mich), eine ausführliche Implementierung anhand eines Autovermietungs-Beispiels, eine Definition von Schritten zur Umsetzung in Form einer Liste sowie weitere Anwendungsbeispiele und Fallstricke. Auf durchschnittlich 10 Seiten pro Muster erhält man auch ein wunderbares Nachschlagewerk zu insgesamt 28 Design Pattern (wenn ich mich nicht verzählt habe).

Durch das Informatikstudium und dem täglichen Programmieren im Beruf waren mir zwar 90% der Pattern bereits bekannt, aber es ist schön zu sehen dass man recht viele davon bereits aktiv anwendet. Viele der Pattern werden auch durch mein aktuell favorisiertes Framework aus dem Hause Zend vorgelebt. Im Buch werden verschiedene Frameworks vorgestellt, es ist interessant zu sehen wie diese die Pattern umsetzen. Vor 2 Jahren hätten mir Dinge wie Front-Controller oder Intercepting-Filter nichts gesagt, denn in der Uni lernt man nur die etwas älteren Pattern der “Gang of Four” (GoF).

Es werden auch bei vielen Pattern aktuell verfügbare Lösungen erwähnt, beispielsweise Frameworks die ein bestimmtes Pattern umsetzen (Symfony, Zend Framework, Propel, Stubbles), aber leider auch einige veraltete PHP4 PEAR-Pakete oder die PHP4-Template-Engine patTemplate. Ich habe gerade nicht für jedes erwähnte PEAR-Paket eine zeitgemäße Alternative parat, aber das war etwas enttäuschend, auch als dann extra für diesen veralteten Code das Error-Reporting geändert werden musste damit keine Notices und Deprecated-Warnungen auftreten. Verbesserungswürdig, ebenso wie mir einige wenige Rechtschreibfehler aufgefallen sind.

Was ich vermisst habe ist zumindest die Erwähnung von einigen wichtigen Anti-Pattern, das hätte gut zum Thema gepasst und zu weiteren Recherchen anregen können. Außerdem fand ich es schade dass schon wieder eine Autovermietung als Beispiel gewählt wurde. Es kann doch nicht so schwer sein, sich mal ein anderes Beispiel auszudenken, es muss doch nicht immer eine Autovermietung oder ein Blogsystem sein.

Diese Missstände sollen aber nicht die sonstige Qualität und Wichtigkeit dieses Buches schmälern. Auf jeden Fall eine klare Empfehlung für jeden PHP-Programmierer, der an etwas größeren Projekten (mit)arbeitet, für PHP-Anfänger ist es sicher eine Nummer zu hoch. Ideal ist es natürlich wenn man bereits objektorientiert programmiert und in der Praxis schon auf diverse Probleme gestoßen ist, die sich nun mit Hilfe dieser generellen Vorgehensweisen besser lösen lassen, und man das Erlernte direkt anwenden kann. Definitiv eines der besten 5 Bücher, die sich mit PHP für Fortgeschrittene beschäftigen.

Written by Michael Kliewe

März 5th, 2010 at 8:28 am

Dateidownload via PHP mit Speedlimit und Resume

with 14 comments

Wenn man statische Dateien zum Download anbieten möchte lässt man das meistens den Webserver erledigen. Dann sind auch häufig Funktionalitäten wie ein Speedlimit oder das Wiederaufnehmen des Downloads (Resume) möglich.

Doch was macht man, wenn etwas mehr Kontrolle nötig ist, beispielsweise eine Authentifizierung in PHP, die Datei erst in Echtzeit berechnet werden muss oder die Datei aus einer Datenbank kommt (böse!). Oder man hat keinen Zugriff auf die Konfiguration des Webservers. Dann kommt man nicht um ein kleines PHP-Script drumherum, muss sich dann aber um die Speedlimit-/Resume Funktionalitäten selbst kümmern. Beispielsweise kann man Premium-Usern mehr Downloadgeschwindigkeit geben als normalen Benutzern, oder pro Benutzer ein tägliches Gesamtlimit festlegen.  Und so schwer ist das garnicht.

Es wird die Extension FileInfo benötigt, seit PHP 5.3.0 Standard, für ältere Versionen kann sie aus der PECL installiert werden. Man kann die entsprechende Stelle auch umgehen und anders lösen falls man FileInfo nicht nutzen möchte/kann.

Es gibt dafür zwar das PEAR-Paket HTTP_Download, aber das ist veraltet (PHP4-Code) und wirft Notices und E_STRICT Meldungen.

Hier mein Script (auf das wesentliche gekürzt, damit es hier in den Blog passt):

<?
class RatedSender
{
	/**
	 * Send a file as download to the browser (maybe limited in speed)
	 *
	 * @param string $filePath
	 * @param int $rate speedlimit in KB/s
	 * @return void
	 */
	public function send($filePath, $rate = 0)
	{
		// Check if file exists
		if (!is_file($filePath)) {
			throw new Exception('File not found.');
		}

		// get more information about the file
		$filename = basename($filePath);
		$size = filesize($filePath);
		$finfo = finfo_open(FILEINFO_MIME);
		$mimetype = finfo_file($finfo, realpath($filePath));
		finfo_close($finfo);

		// Create file handle
		$fp = fopen($filePath, 'rb');

		$seekStart = 0;
		$seekEnd = $size;

		// Check if only a specific part should be sent
		if(isset($_SERVER['HTTP_RANGE'])) {
			// If so, calculate the range to use
			$range = explode('-', substr($_SERVER['HTTP_RANGE'], 6));

			$seekStart = intval($range[0]);
			if ($range[1] > 0) {
				$seekEnd = intval($range[1]);
			}

			// Seek to the start
			fseek($fp, $seekStart);

			// Set headers incl range info
			header('HTTP/1.1 206 Partial Content');
			header(sprintf('Content-Range: bytes %d-%d/%d', $seekStart, $seekEnd, $size));
		}
		else {
			// Set headers for full file
			header('HTTP/1.1 200 OK');
		}

		// Output some headers
		header('Cache-Control: private');
		header('Content-Type: ' . $mimetype);
		header('Content-Disposition: attachment; filename="' . $filename . '"');
		header('Content-Transfer-Encoding: binary');
		header("Content-Description: File Transfer");
		header('Content-Length: ' . ($seekEnd - $seekStart));
		header('Accept-Ranges: bytes');
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($filePath)) . ' GMT');

		$block = 1024;
		// limit download speed
		if($rate > 0) {
			$block *= $rate;
		}

		// disable timeout before download starts
		set_time_limit(0);

		// Send file until end is reached
		while(!feof($fp))
		{
			$timeStart = microtime(true);
			echo fread($fp, $block);
			flush();
			$wait = (microtime(true) - $timeStart) * 1000000;

			// if speedlimit is defined, make sure to only send specified bytes per second
			if($rate > 0) {
				usleep(1000000 - $wait);
			}
		}

		// Close handle
		fclose($fp);
	}
}

try {
	$ratedSender = new RatedSender();
	$ratedSender->send('data.zip', 3);
} catch (Exception $e) {
	header('HTTP/1.1 404 File Not Found');
	die('Sorry, an error occured.');
} ?>

Das Script unterstützt durch die RANGE Angabe sowohl Resume als auch den parallelen Download einer einzelnen Datei mit mehreren Threads, wie es mit vielen Download-Managern möglich ist. In diesem Beispiel wird also jede Verbindung auf 3 KB/s beschränkt. Möchte man dieses Limit nicht pro Verbindung sondern pro User/IP-Adresse setzen muss man das in PHP errechnen.

Viele Downloadscripte sind unsicher da man z.B. via GET-Parameter den Dateinamen angeben kann, dort sollte man sehr aufpassen dass man nur Dateien zum Download anbietet die auch wirklich herunterladbar sein sollen.

Verbesserungsvorschläge sind natürlich willkommen!

Written by Michael Kliewe

März 4th, 2010 at 9:16 am

Verschiedene Möglichkeiten, mit PHP zu twittern

with 10 comments

Twitter lebt natürlich von manuell eingetippten Kommentaren, aber ab und zu ist es auch sinnvoll und nötig, automatisiert Tweets zu senden. Das geht dank der vielseitigen API auf verschiedensten Wegen, wovon ich einige hier vorstellen möchte.

Den Anfang macht eine Funktion von Fabien Potencier. Zur Nutzung dieser Funktion ist keine Extension oder Framework nötig, es werden nur PHP-Funktionalitäten verwendet:

function tweet($message, $username, $password)
{
  $context = stream_context_create(array(
    'http' => array(
      'method'  => 'POST',
      'header'  => sprintf("Authorization: Basic %s\r\n", base64_encode($username.':'.$password)).
                   "Content-type: application/x-www-form-urlencoded\r\n",
      'content' => http_build_query(array('status' => $message)),
      'timeout' => 5,
    ),
  ));
  $ret = file_get_contents('http://twitter.com/statuses/update.xml', false, $context);

  return false !== $ret;
}
tweet($message, $username, $password);

Eine sehr ähnliche Lösung umgesetzt mit Hilfe der PHP-Extension curl:

function postTweet($message, $username, $password)
{
	$url = 'http://twitter.com/statuses/update.json';
	$fld = http_build_query(array('status' => $message));
	$ch = curl_init();

	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_USERPWD, $username.':'.$password);
	curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_POST, 1);
	curl_setopt($ch, CURLOPT_POSTFIELDS, $fld);

	$ret = curl_exec($ch);

	return false !== $ret;
}
postTweet($message, $username, $password);

Eine andere Methode ist die Verwendung der Zend-Framework-Klasse Zend_Service_Twitter.

$twitter = new Zend_Service_Twitter($username, $password);
$response = $twitter->status->update($message);

Natürlich kann man auch seine Twitter-Timeline via API abrufen, aber das könnt ihr euch selbst nachschauen wie das geht. Nagut, als Stichworte seien friendsTimeline() und userTimeline() gegeben ;-)

Auf der Linux-Kommandozeile kann man natürlich auch auf Twitter zugreifen, hier beispielsweise die Abfrage der Freundes-Timeline:

curl -u username:password  http://twitter.com/statuses/friends_timeline.xml

Viele weitere Funktionen und Klassen findet man im API-Wiki von Twitter.

Written by Michael Kliewe

März 3rd, 2010 at 9:15 am

Lesepool Nummer 1

with 4 comments

Wer über das Wochenende nicht bereits genug ausgelastet ist, kann hier noch einige interessante Artikel und Webseiten lesen, die ich so in den letzten Tagen gesammelt habe:

PHP:
Month of Bugs in PHP: May 2010
http://www.suspekt.org/2010/02/19/sneak-preview-month-of-php-security-2010/
Symfony 2 soll das schnellste Framework sein? Padraic Brady beschleunigt das Zend Framework
http://blog.astrumfutura.com/archives/421-PHP-Framework-Benchmarks-Entertaining-But-Ultimately-Useless.html
Changelog Zend Framework 1.10.2
http://framework.zend.com/changelog/1.10.2
Man darf doch träumen: Browser Feature Wunschliste
http://www.stevesouders.com/blog/2010/02/15/browser-performance-wishlist/
Das Hacken hat begonnen: HipHop auf GitHub
http://github.com/facebook/hiphop-php

Systeme:
http://www.rootforum.org/wiki/Vergleich_der_Anbindung_von_PHP_als_Apache-Modul_CGI_und_FastCGI_%28de%29

Witziges für Entwickler:
Yeehaa, wir hassen den Internet Explorer!
http://www.chigarden.com/2007/10/tutorial-making-the-ie-voodoo-doll/
Hatte ich ja bereits via Twitter gepostet, aber es ist einfach gut:
http://www.worktobejudged.com/strippause/peca.html

Ich hoffe, dass ich es regelmäßig schaffe, Links zu posten, aber es wird wohl nicht jede Woche sein denke ich, mal sehen.

Written by Michael Kliewe

Februar 26th, 2010 at 11:31 am

Posted in PHP

Tagged with ,

10 Besucher online
10 Gäste, 0 Mitglied(er)
Meiste Besucher heute: 11 um/am 01:09 am CET
Diesen Monat: 51 um/am 03-09-2010 03:03 pm CET
Dieses Jahr: 55 um/am 02-01-2010 09:48 am CET
Jederzeit: 55 um/am 02-01-2010 09:48 am CET