PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Archive for the ‘PHP’ tag

Wie erstelle ich einen Socket-Server in PHP?

with 23 comments

In einem meiner letzten Artikel über Windows-Dienste habe ich ja bereits angesprochen, dass auch Dienste aller Art in PHP realisiert werden können. In jenem Artikel erwähnte ich auch, dass es bereits Webserver, FTP-Server, DNS-Server etc. in PHP gibt.

Heute zeige ich euch, wie man das machen kann. Grundsätzlich haben all diese Dienste gemeinsam, dass sie auf einem TCP-Port auf Verbindungen warten, und dort im Verbindungsfall Befehle entgegennehmen, Aktionen durchführen und Daten zurückliefern können.

Hier möchte ich einen kleinen Chat-Server erstellen, mit dem man sich verbinden kann, und mit allen anderen verbundenen Clients chatten kann. Dieser Chat-Server soll auch gleich als Dienst permanent laufen.

Dazu benötigen wir sogenannte Socket-Funktionen, die PHP seit Version 4.3 bietet. Die entsprechende Extension ist bereits in PHP enthalten, muss aber evtl. in der php.ini noch aktiviert werden:

extension=php_sockets.dll

Dann kann es auch schon loslegen. Das grobe Konzept: Wir erstellen ein Socket und lassen diesen auf Port 33380 lauschen. Wenn eine Verbindung reinkommt, begrüßen wir den neuen Benutzer und fügen ihn einem Array hinzu. Sollte sich ein weitere Benutzer verbinden, tun wir natürlich das selbe. Schreibt ein Benutzer irgendetwas, wird es an alle anderen Benutzer broadcasted. Es soll auch Spezial-Kommandos geben: mit „exit“ oder „quit“ trennt sich der Benutzer vom Server. Mit „term“ stoppt er den Chatserver.

Genug geschwatzt, hier der Code der Klasse:

class SocketChatServer {
	private $address = '0.0.0.0';	// 0.0.0.0 means all available interfaces
	private $port = 33379;			// the TCP port that should be used
	private $maxClients = 10;

	private $clients;
	private $socket;

	public function __construct() {
		// Set time limit to indefinite execution
        set_time_limit(0);
        error_reporting(E_ALL ^ E_NOTICE);
	}

    public function start() {
    	// Create a TCP Stream socket
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        // Bind the socket to an address/port
        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
		socket_bind($this->socket, $this->address, $this->port);
		// Start listening for connections
		socket_listen($this->socket, $this->maxClients);


		$this->clients = array('0' => array('socket' => $this->socket));

	    while (true) {
	        // Setup clients listen socket for reading
		    $read[0] = $this->socket;
			for($i=1; $i<count($this->clients)+1; ++$i) {
				if($this->clients[$i] != NULL) {
					$read[$i+1] = $this->clients[$i]['socket'];
				}
			}

			// Set up a blocking call to socket_select()
			$ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL);

			/* if a new connection is being made add it to the client array */
			if(in_array($this->socket, $read)) {
				for($i=1; $i < $this->maxClients+1; ++$i) {
					if(!isset($this->clients[$i])) {
						$this->clients[$i]['socket'] = socket_accept($this->socket);
						socket_getpeername($this->clients[$i]['socket'], $ip);
						$this->clients[$i]['ipaddy'] = $ip;

						socket_write($this->clients[$i]['socket'], 'Welcome to my Custom Socket Server'."\r\n");
						socket_write($this->clients[$i]['socket'], 'There are '.(count($this->clients) - 1).' client(s) connected to this server.'."\r\n");

						$this->log("New client #$i connected: " . $this->clients[$i]['ipaddy']);
						break;
					} elseif($i == $this->maxClients - 1) {
						$this->log('Too many Clients connected!');
					}

					if($ready < 1) {
						continue;
					}
				}
			}

			// If a client is trying to write - handle it now
			for($i=1; $i<$this->maxClients+1; ++$i) {
				if(in_array($this->clients[$i]['socket'], $read)) {
					$data = @socket_read($this->clients[$i]['socket'], 1024, PHP_NORMAL_READ);

					if($data === FALSE) {
						unset($this->clients[$i]);
						$this->log('Client disconnected!');
						continue;
					}

					$data = trim($data);

					if(!empty($data)) {
						switch ($data) {
							case 'exit':
							case 'quit':
								socket_write($this->clients[$i]['socket'], "Thanks for trying my Custom Socket Server, Goodbye.\r\n");
								$this->log("Client #$i is exiting");
								unset($this->clients[$i]);
								continue;
							case 'term':
								// first write a message to all connected clients
								for($j=1; $j < $this->maxClients+1; ++$j) {
									if(isset($this->clients[$j]['socket'])) {
										if($this->clients[$j]['socket'] != $this->socket) {
											socket_write($this->clients[$j]['socket'], "Server will be shut down now...\r\n");
										}
									}
								}
								// Close the master sockets, server termination requested
								socket_close($this->socket);
								$this->log("Terminated server (requested by client #$i)");
								exit;
							default:
								for($j=1; $j < $this->maxClients+1; ++$j) {
									if(isset($this->clients[$j]['socket'])) {
										if(($this->clients[$j]['socket'] != $this->clients[$i]['socket']) && ($this->clients[$j]['socket'] != $this->socket)) {
											$this->log($this->clients[$i]['ipaddy'] . ' is sending a message to ' . $this->clients[$j]['ipaddy'] . '!');
											socket_write($this->clients[$j]['socket'], '[' . $this->clients[$i]['ipaddy'] . '] says: ' . $data . "\r\n");
										}
									}
								}
								break(2);
						}
					}
				}
			}
	    } // end while
    }

    private function log($msg) {
    	// instead of echoing to console we could write this to a database or a textfile
        echo "[".date('Y-m-d H:i:s')."] " . $msg . "\r\n";
    }
}

Dieser Code funktioniert sowohl unter Windows als auch unter Linux und kann dort als Dienst installiert werden.

Hier einige Screenshots von Clients und vom Server:

chatserver2

chatserver1

chatserver3

Man kann natürlich noch leicht weitere Kommandos implementieren, wie zB ein kleiner Login für Admins, die Liste aller Clients anzeigen, einzelne Clients kicken, die Konfiguration ändern (dann müßte man sie in eine .ini auslagern und neu laden können) usw.
Man könnte auch eine Admin-Webseite implementieren, d.h. wenn man sich mit einem Browser auf den Port verbindet, erkennt das der Dienst und bietet eine html-Oberfläche mit diverse Admin-Funktionen.

Es gibt viele Dinge, die man so realisieren kann. Beispielsweise ein Online-Multiplayer-Spiel in der Konsole oder sogar mit grafischer Java-Oberfläche, eine eigene Steuerung für seinen Server im Keller, einen eigenen Mailserver, ein Backend für Flash-Casual-Games (wenn HTTP zuviel Overhead hat oder zu langsam ist) und und und.

Wie PHP bei einer sehr großer Anzahl von Verbindungen skaliert weiß ich nicht, kann ja jemand von euch mal ausprobieren 😉

Written by Michael Kliewe

August 24th, 2009 at 9:04 pm

Posted in PHP

Tagged with , ,

LDAP Authentifizierung und andere Abfragen

with 6 comments

Zuhause beim Programmieren oder bei der Erstellung von einfachen Webseiten wird man mit LDAP wahrscheinlich nicht viel zu tun haben, doch wenn man häufiger Intranet-Projekte erstellt oder in irgendeiner Weise den Login an ein OpenLDAP-Verzeichnis oder ActiveDirectory knüpfen will, kommt man an LDAP kaum vorbei.

Grundsätzliche Informationen über LDAP und ActiveDirectory findet man natürlich bei Wikipedia. Ich möchte hier nicht 5 Seiten darüber schreiben, wofür soetwas gut ist und warum man sowas haben sollte, da könnt ihr euch am besten selbst die Informationen sammeln. Stichworte sind wie gesagt LDAP, ActiveDirectory, OpenLDAP und für die hart gesottenen die RFC 2307.

Im Firmenumfeld werden Administrationsseiten (von denen es in größeren Firmen schnell Unmengen gibt) natürlich auch geschützt, sodass nur die berechtigten Personen darauf zugreifen können. Da man nicht für jede Seite ein eigenes Passwort etc. verwenden möchte, nutzt man das vorhandene ActiveDirectory, um Zugriffe auf die Seiten zu vergeben. Außerdem ist es damit einfacher, bei Ausscheiden eines Mitarbeiters zentral an einer Stelle den Account zu löschen.

Nur wie macht man das? Wie gestatte ich einem ActiveDirectory-User den Zugriff, wohingegen ich anderen den Zugriff verweigere?

Es gibt mehrere Lösungen. Zum einen bietet der Webserver da Möglichkeiten. Beim IIS zum Beispiel kann man bei der Konfiguration der Website entweder anonymen Zugriff erlauben (d.h. jeder Besucher kann sie betreten), oder man aktiviert eine Passwortabfrage, wie zB:

ldap1

Falls man dann die entsprechende Webseite betreten möchte, kommt eine Abfrage:

ldap2

Damit ist schonmal sichergestellt, dass man nur mit einen gültigen Account Zutritt erlangt. Doch wie bestimme ich nun, dass nur eine Untermenge aller Accounts zugelassen werden soll? Im Falle des IIS macht man das mit Hilfe der NTFS-Berechtigungen. Wenn man auf das DocumentRoot nur denjenigen Leserechte gibt, die auch die Seite betreten können sollen, haben wir genau das, was wir wollen.

Auch der Apache kann natürlich etwas vergleichbares. Dazu benötigt man das Modul mod_auth_ldap. Hier eine einfache Beispielkonfiguration:

<Location /example-repository>
    # LDAP soll für die Authentifizierung zuständig sein.
    AuthLDAPAuthoritative on

    AuthType Basic
    AuthName "Mein geschütztes Verzeichnis"

    # Wenn anonyme Zugriffe auf nicht erlaubt sind müssen sie hier
    # den DN für einen Benutzer angeben, der für den Lesezugriff
    # verwendet werden kann.
    AuthLDAPBindDN "CN=browse_user,OU=FunktionaleUser,DC=example,DC=com"
    # Das Passwort für den „Browse User“
    AuthLDAPBindPassword sicheres_passwort

    # Die LDAP URL für die Verbindung.
    # Alle Verzeichnisse unterhalb der angegebenen „Bind URL“ werden
    # durchsucht. Das Feld „login“ wird für die Suche nach dem Benutzernamen verwendet.
    # Format: scheme://host:port/basedn?attribute?scope?filter
    AuthLDAPURL "ldap://ldap.example.com:389/DC=example,DC=com?login?sub?(objectClass=*)"

    # Natürlich ist auch eine gesicherte Verbindung möglich. Beispiel:
    # "ldaps://ldap.example.com:636/DC=example,DC=com?login?sub?(objectClass=*)"

    Require valid-user
</Location>

Weitere Informationen dazu gibt es natürlich auf der entsprechenden Webseite zu mod_auth_ldap.

Dies sind also Methoden, die die Webserver allein regeln, PHP bekommt davon garnichts mit. Falls die Authentifizierung erfolgreich war (Der Login ist korrekt und dieser User hat Leserechte), kann man in PHP mittels der Variablen $_SERVER[‚AUTH_USER‘] den Benutzernamen herausfinden.

Um noch flexibler zu sein, kann man diese Authentifizierung natürlich auch direkt in PHP erledigen. Dazu bietet php einige ldap_* Funktionen, die es uns ermöglichen, via LDAP das Verzeichnis zu durchsuchen und Informationen daraus auszulesen. Außerdem kann man sich dann ein formschönes Login-Formular basteln, und hat nicht so einen grauen Kasten.

Um sich mit einem LDAP-Server verbinden zu können, benötigt man die Serverdaten, die Protokollversion und natürlich einen Login für den LDAP-Server, das ist der Bind-User. Das sieht dann ungefähr so aus:

function setupLdapConnection() {
	// get ldap connection to domainX
	$ldapOptions = array (
				'binddn'    => 'cn=ldapsearch,ou=serviceuser,DC=domainX,DC=net',
				'bindpw'    => 'ldapsearchpwd',
				'basedn'    => 'DC=domainX,DC=net',
				'host'      => 'ldap.domainX.net'    );
	
	$ldap = ldap_connect($ldapOptions['host']);
	if ($ldap == false) {
		throw new Exception('LDAP connnect failed');
	}
	
	ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
	ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
	
	$res = ldap_bind($ldap, $ldapOptions['binddn'], $ldapOptions['bindpw']);
	if ($res === false) {
		throw new Exception('Unable to bind to LDAP server');
	}
	
	return $ldap;
}

Damit haben wir nun eine LDAP-Connection aufgebaut, und dann kann man anfangen zu Suchen, beispielsweise so:

$ldap = setupLdapConnection();

$baseDn = 'DC=domainX,DC=net';
$attributes = array('cn', 'mail', 'objectClass', 'sAMAccountName', 'extensionAttribute13');
$filter = 'cn=User123';

$res = ldap_search($ldap, $baseDn, $filter, $attributes);

if ($res === false) {
	return "LDAP search failed\r\n";
}

if (ldap_count_entries($ldap, $res) === 0) {
	return "LDAP search failed, no entries found\r\n";
}

$entry = ldap_get_entries($ldap, $res);
if ($entry === false) {
	return "LDAP get entry failed\r\n";
}

return $res;

In $res haben wir dann das Ergebnis-Array mit den gewünschten Informationen. Man kann natürlich auch mit Hilfe der $baseDn nur in einem bestimmten Zweig suchen, oder mit Hilfe des $filter nach anderen Attributen suchen.

Mit diesen Funktionen kann man dann auch zB prüfen, ob ein User Mitglied einer Gruppe ist (Attribut memberOf), oder seine Email-Adresse herausfinden oder oder…

Zur Authentifizierung selbst gibt es natürlich auch schon fertige Klassen. Zu nennen sind da wohl PEAR_Auth und Zend_Auth_Adapter_Ldap. Hier ein schönes kurzes Beispiel mit dem ZF:

application.ini:

ldap.server1.host = ldap.domainX.net
ldap.server1.useSsl = false

ldap.server1.accountDomainName = domainX.net
ldap.server1.accountDomainNameShort = domainX
ldap.server1.accountCanonicalForm = 3
ldap.server1.accountFilterFormat = "(&(objectClass=user)(sAMAccountName=%s))"

ldap.server1.username = "cn=ldapsearch,ou=serviceuser,DC=domainX,DC=net"
ldap.server1.password = ldapsearchpwd
ldap.server1.baseDn = "DC=domainX,DC=net"
ldap.server1.bindRequiresDn = true

IndexController:

public function loginAction() {
	$form = new forms_LoginForm();

    if ($this->getRequest()->isPost()) {
    	$formData = $this->getRequest()->getPost();
        if ($form->isValid($formData)) {
			$values = $form->getValues();
				
			$loginSuccessful = false;
			$auth = Zend_Auth::getInstance();
			
			$options = Zend_Registry::get('configIni')->ldap->toArray();
			
			$adapter = new Zend_Auth_Adapter_Ldap($options, 
												$values['loginusername'], 
												$values['loginpassword']);
			$result = $auth->authenticate($adapter);

			if ($result->isValid()) {					
				$namespace = new Zend_Session_Namespace();
				$namespace->username = $values['loginusername'];				
			} else {
				$this->_flashMessenger->addMessage('error:login_failed_ldap');					
			}
			$this->_redirect("");				
		} else {
			$form->populate($formData);
		}
               
    }
    $this->view->form = $form;
}

Ist natürlich alles auf das Wesentliche gekürzt, man hat normalerweise natürlich noch diverse Sicherheitsabfragen oder zB einen try-catch-Block um das authenticate() etc.

Nutzt ihr auch LDAP, und wenn ja wie?

Written by Michael Kliewe

August 7th, 2009 at 4:06 pm

Sinnvolle SVN Hooks für PHP Projekte

with 11 comments

Dieser Artikel ist nur für diejenigen gedacht, die SVN bereits kennen. Wer jetzt die Stirn runzelt, möge sich vorher bei wikipedia oder youtube informieren und mal erste Versuche mit einem SVN-Server sammeln.

Erstmal zur Begrifflichkeit ansich: Hooks sind Interfaces zu kleinen externen Programmen, die zu bestimmten Zeiten während eines Programmablaufs aufgerufen werden können. Sie klinken sich also in den Ablauf ein.

Beim SVN gibt es 3 interessante Hooks, die häufig genutzt werden (insgesamt gibt es 9):

– start-commit
– pre-commit
– post-commit

Im SVN Handbuch kann man genauer nachlesen, wann diese Hooks aufgerufen werden.

Das erste Script, welches wir als pre-commit-Hook aufrufen wollen, ist ein einfaches PHP-Lint, wir wollen also die PHP-Syntax testen. Dieses Beispiel mache ich etwas ausführlicher, alle anderen Hook-Scripte hänge ich einfach an dieses Posting dran.

#!/bin/bash
REPOS="$1"
TXN="$2"

PHP="/usr/local/php5/bin/php"
SVNLOOK="/usr/bin/svnlook"
AWK="/usr/bin/awk"
GREP="/bin/egrep"
SED="/bin/sed"

CHANGED=`$SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}' | $GREP \\.php$`

for FILE in $CHANGED
do
    MESSAGE=`$SVNLOOK cat -t "$TXN" "$REPOS" "$FILE" | $PHP -l`
    if [ $? -ne 0 ]
    then
        echo 1>&2
        echo "***********************************" 1>&2
        echo "PHP error in: $FILE:" 1>&2
        echo `echo "$MESSAGE" | $SED "s| -| $FILE|g"` 1>&2
        echo "***********************************" 1>&2
        exit 1
    fi
done

Da ich mein SVN auf einem Linux-Server betreibe, habe ich hier das entsprechende Bash-Script. Falls ihr euren SVN-Server unter Windows betreibt, muß man das Script natürlich anpassen.

Was passiert hier? Es werden mittels svnlook alle geänderten oder neu hinzugefügten Dateien gesucht, aufgelistet und dann noch die Dateien mit einer .php Endung gefiltert. Für jede Dieser Dateien wird dann wiederum via svnlook der Quelltext geholt und mittels der Pipe an „php -l“ übergeben. Im Falle eines Fehlers gibt es eine Fehlermeldung, die dann im SVN-Client ausgegeben wird. Der Commit wird also scheitern (da dies ja ein pre-commit-Hook ist).

Ein weiteres Hook-Script, welches pre-commit ausgeführt wird, ist zum Beispiel der PEAR PHP-CodeSniffer. Dieses kleine Script kann PHP-Code auf Coding-Standards überprüfen, also ob beispielsweise PHPdoc vorhanden ist, oder ob die geschweiften Klammern an den richtigen Stellen stehen. Ich persönlich habe dieses Script allerdings nicht als Hook eingebunden, da ich auch ab und zu fremden Code ins SVN packe, der natürlich nicht meinen Code-Standards entspricht. PHP-CodeSniffer führe ich lokal ab und zu aus, und dann auch nur auf meine Verzeichnisse. Beim CodeSniffer wird ein entsprechendes Hook-Script gleich mitgeliefert.

Noch ein einfaches kleines Hook-Script wäre das hier:

/*
  test to see if svn commit comment length is greater than or equal to 10 chars
*/
$log = exec("svnlook log -t ". $argv[2] ." ". $argv[1]);
if(strlen($log) > 9){
      exit(0);
}else{
      exit(1);
}
---------

Weitere Hook-Scripte:

Falls ihr noch andere Hook-Scripte habt, nur her damit!

EDIT: Stefan empfiehlt noch ein tolles Hook-Script, mit dem man automatisch nach einem Commit die geänderten Dateien auf einen FTP, SFTP oder Filesystem synchronisieren kann: http://svn2web.sourceforge.net

Written by Michael Kliewe

Juli 16th, 2009 at 12:24 pm

Posted in PHP

Tagged with , , ,

PHP Profiling mit XDebug und KCachegrind

with 6 comments

So, nun will ich das Thema nachholen, was ich am letzten Donnerstag hab anklingen lassen. Am Wochenende ist dann jedoch das Gewinnspiel dazwischengekommen, sodass ich nun etwas zum Thema PHP-Profiler schreibe.

Profiler? Sind das nicht die FBI-Psychiater, die anhand von Tatorten und den Opfern etwas über den Täter aussagen können, wie er lebt und denkt? Naja, vielleicht…

Profiler gibt es auch für die meisten Programmiersprachen. Profiler schauen „unter die Haube“ und untersuchen die kleinsten Einheiten und Funktionen einen PHP-Programms. Sie messen die Zeiten für die Kommandos, zählen die Anzahl der Aufrufe, merken sich welche Funktion andere Funktionen aufruft und kann daraus sehr ausführliche und interessante Tabellen und Graphen generieren. Diese nutzt man dann am häufigsten, um (zeitliche) Flaschenhälse bei der Programmierung zu finden.

Aber wie erhält man diese Zeiten? Wenn man nur einen kleinen Teil seines Programms beobachten möchte und die Zeit messen möchte, die es braucht, macht man das wahrscheinlich so:

$start = microtime(true);
 // Hier einige Befehle, die gemessen werden sollen
 $end = microtime(true);
 $diff = $end - $start;
 echo "Benötigte Zeit: " . $diff;

Das ist meistens völlig ausreichend für den Anfang, um einen kurzen Überblick zu finden. Wenn das Programm aber sehr groß ist, und man nicht tausende dieser Messungen einbauen möchte, kann man gleich das ganze Script profilen lassen.

Wir machen das ganze mal praktisch an einem kleinen Beispiel:

class User
{
	private $username;
	private $newsletter;

	public function __construct() {
		$this->username = $this->getRandomString(8);
		$this->newsletter = rand(0, 1);
	}

	public function getUsername() {
		usleep(500000);
		return $this->username;
	}

	public function getNewsletter() {
		return $this->newsletter;
	}

	private function getRandomString($stringLength) {
	    //srand ((double)microtime() * 1000000);
	    return substr(md5(rand()), 0, $stringLength);
	}
}

for ($i = 0; $i < 5; $i++) {
	$user = new User();
	if (rand(0, 1)) {
		echo $user->getUsername()."\n";
	} else {
		echo $user->getNewsletter()."\n";
	}
}

Nun muß ich noch XDebug in der php.ini aktivieren. Dazu aktiviert man einfach die PHP-Extension XDebug wie folgt in der php.ini:

[XDebug]
; Only Zend OR (!) XDebug
zend_extension_ts=“C:\xampp\php\ext\php_xdebug.dll“
xdebug.remote_enable=true
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir=“C:\xampp\tmp“

Natürlich sollte man vorher die php_xdebug.dll downloaden und in den entsprechenden Ordner legen. Nachdem man die php.ini so geändert hat, wird für jedes PHP-Script ein sogenannter CacheGrind-Dump im Output-Dir abgelegt. Diese Datei kann, je nach Komplexität und Umfang des Script, auch mehrere hundert MB groß werden.

Nun führe ich das Script ganz normal aus, im Hintergrund wird dann die CacheGrind Datei erzeugt.

run

Die generierte cachegrind.out.2916 sieht so aus, und ist (noch) nicht wirklich brauchbar:

cachegrundtxt

Öffnen und tabellarisch bzw. grafisch darstellen kann man diese Textdatei dann zum Beispiel mit dem Windows Programm WinCacheGrind:

wincachegrind2916

Hier erkennt man zwar schon etwas, aber viel besser ist unter Linux KCachegrind, das sieht dann so aus:

kcachegrind_2916

Man schaue sich nun die Anzahl der Aufrufe sowie die Zeitwerte an, und kann unzweifelhaft feststellen, dass in unserem einfachen Beispiel die usleep-Funktion die meiste Zeit gebraucht hat. Aber auch getRandomString() ist nicht zu vernachlässigen, vielleicht könnte man da noch etwas optimieren. Bei großeren Programmen mit SQL-Abfragen und komplexeren Algorithmen und Abläufen wird das ganze natürlich noch viel interessanter, probiert es einfach mal bei euren großen Projekten aus!

Man kann sowohl nach den absoluten als auch den prozentualen Zeitwerten sortieren, sieht Callees und Caller, kann sich einen Call Graph anzeigen lassen usw, ein wirklich tolles Programm.

Ich würde auf jeden Fall für ernsthafte Profiler das KCachegrind empfehlen. Falls man gerade kein laufendes Linux-System zur Hand hat, ist es höchste Zeit, mittels VirtualBox und Ubuntu schnell eins aufzusetzen. Beides ist natürlich kostenlos, und nach einer Stunde ist es lauffähig. Man braucht auch keine umständlichen Dual-Boot-Sachen und zerschießt sich wohlmöglich sein System, VirtualBox kann „ein Betriebssystem in einem Fenster“ laufen lassen. Einfach mal ausprobieren und informieren.

Mittels Profiling kann man sehr einfach herausfinden, wie oft welche Funktion aufgerufen wird. Man erkennt ziemlich schnell, wieviele Datenbank-Queries gemacht werden und von wo diese kommen. Man sieht, wieviele Objekte erstellt werden und wo dies passiert (und wie lang das dauert).

Die gefundenen Flaschenhälse kann man dann beseitigen. Entweder durch effizientere SQL-Abfragen, bessere Algorithmen, Caching oder oder. Das ist von Fall zu Fall verschieden.

Mich würde interessieren, ob und wie ihr profiled, und was ihr dadurch für gewöhnlich an Fehlern/Flaschenhälsen findet und wie ihr sie behebt.

Written by Michael Kliewe

Juli 15th, 2009 at 9:13 am

Posted in PHP

Tagged with , , ,

Gewinner des Buches steht fest

with 9 comments

So, ich habe es doch noch geschafft, der Gewinner der Verlosung steht fest. Fix einen schönen Zufallsgenerator geschrieben (natürlich in PHP mit dem Zend Framework, Dojo und ein bischen Javascript), und dann das ganze auch noch aufgenommen. Die php-Dateien gibts natürlich auch zum Download, falls ihr auch mal einen so schönen Zufallsgenerator braucht 😉

(Also das .swf bzw .flv, was mein Programm generiert hat, ist ja mal zum fürchten. Mit welchen Programmen nehmt ihr Desktop-Flash-Movies auf?) Hier gibts das avi als Download (Rechtsklick -> Speichern unter).

[flv:https://www.phpgangsta.de/wp-content/uploads/randomizer.flv 702 544]

——-

Der Gewinner: Ghost! (Die Nummer 2). Herzlichen Glückwunsch!

Bitte schick mir deine Adresse via Email rüber an mail, dann schick ich es dir zu.

Danke auch an alle, die mitgemacht haben!

——-

Download des Zufallsgenerators als zip-Datei. Nicht mit reingepackt habe ich das Zend Framework und Dojo, das muß noch an die richtigen Stellen gelegt werden damit es funktioniert (library und public/js )

Written by Michael Kliewe

Juli 14th, 2009 at 1:03 am

Posted in Allgemein

Tagged with , ,