PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Archive for the ‘PHP’ Category

PHP Online IDE: CodeRun

with 3 comments

Hier ein kleiner Tip für euch: Schaut euch mal diese Online IDE von CodeRun an. Dort kann man PHP-Code online im Browser schreiben und auch Laufen lassen. Wenn man dort ein entsprechendes Paket gekauft hat kann man direkt aus der IDE auch Deployen.

Falls man seine Quelltexte veröffentlichen möchte kann man einen Permalink erstellen und andere teilhaben lassen.

Für die tägliche Arbeit an großen Projekten eher nicht zu gebrauchen, aber für kleine Testscripte oder die Zusammenarbeit mit anderen einen Blick wert.

http://www.coderun.com/ide

Written by Michael Kliewe

Februar 8th, 2010 at 9:55 am

Posted in PHP

Tagged with ,

HipHop für PHP

with 7 comments

Gestern war nun der große erhoffte Tag, Facebook wollte seinen neuen „PHP-Compiler“ veröffentlichen. Bis jetzt ist das noch nicht passiert, allerdings gab es ein offizielles Announcement und einen Videostream, in dem Näheres erläutert wurde.

Edit: Auf GitHub findet man mittlerweile den Sourcecode.

Hier ein paar erste Fakten, so wie ich sie bisher gelesen habe:

  • „HipHop for PHP“ übersetzt PHP-Code in C++ Code
  • Danach kann es mittels g++ kompiliert werden und man erhält eine ausführbare Datei
  • Diese Datei enthält auch direkt einen Webserver (basiert auf Libevent) und eine CLI-Schnittstelle. Um sein Projekt vom Internet verfügbar zu machen muss man nur seinen Apache/Lighty/Squid/etc umleiten (Reverse Proxy) auf diesen kleinen Miniwebserver, so kann man dann mehrere Projekte auf einem Port (80) laufen lassen.
  • Beim Übersetzen und Kompilieren wird der Code durch statische Analysen etc. optimiert, und z.B. der beste C++-Datentyp gewählt (PHP ist lose typisiert, C++ streng)
  • Es soll einen Just-In-Time-Compiler geben (HPHPi), mit dem man seine Anwendung testen kann, ohne sie jedes Mal zu übersetzen und zu kompilieren, denn das kann unter Umständen länger dauern.
  • Bei Facebook läuft das ganze seit einigen Monaten produktiv, man spart damit ca. 50% CPU-Last ein
  • Derzeit unterstützt es PHP 5.2 Code, PHP 5.3 wird noch nicht unterstützt. eval() kann nicht genutzt werden.

Facebook, die zweitgrößte Website der Welt, setzt „HipHop for PHP“ nun seit einigen Monaten produktiv ein, 90% des Traffics wird bereits damit abgewickelt, und es läuft wohl stabil. 300.000 Codezeilen und 5000 Unittests sollen es sein, und es wird wohl bald auf GitHub veröffentlicht, aktuell nur für Linux-Systeme (da Facebook auf Linux setzt), aber Windows könnte bald folgen wenn der Open-Source-Code erweitert wird.

Gerade für Firmen könnte das interessant sein, wenn man ein Drittel oder gar die Hälfte seiner PHP-Maschinen einsparen kann, ich bin sehr gespannt.

PHP-Extensions müssen neu geschrieben werden in C++. Wir werden also vielleicht eine zweigeteilte Welt bekommen, HipHop-kompatibler Code und „native PHP Code“. Wie kompatibel ist das ganze mit bestehendem Code, wie kann man dann debuggen, wie sieht dann der Deployment-Prozess aus, wie groß ist der CPU- und Memory-Performance-Vorteil gegenüber APC/XCache wirklich? Wieviele „unserer normalen Projekte“ sind CPU-beschränkt und nicht IO-beschränkt?

Interessant ist das ganze auch für Projekte, die dann eine kompilierte Version ausliefern können, das Deployment könnte sich auch auf eine Übersetzung, Kompilierung und dann Kopie einer Datei beschränken? Wir werden sehen…

Ich hoffe, dass wir in den nächsten Stunden und Tagen da mehr Informationen erhalten und der Sourcecode veröffentlicht wird. Es sind jedenfalls spannende Tage, die die Zukunft von PHP verändern könnten.

Written by Michael Kliewe

Februar 3rd, 2010 at 9:12 am

Posted in PHP

Tagged with , ,

Was ist gerade auf meinem Server los?

with 4 comments

Ich möchte hier ein kleines PHP-Script vorstellen das ich mir vor einiger Zeit mal geschrieben habe um einen schnellen Überblick meines Servers zu bekommen. Ich möchte dazu die letzten Zeilen einiger Logdateien anzeigen lassen. Anstatt mich dazu via SSH zum Server zu verbinden und alle Dateien durchzuschauen (tail -f), habe ich hier eine kleine Admin-Webseite, die mir das ganze auf einen Blick zeigt, und zwar fast in Echtzeit (je nachdem wie man die Refresh-Rate einstellt).

So sieht das ganze dann aus:

Logdateien werden häufig sehr groß, mehrere hundert MB sind keine Seltenheit. Beim Auslesen der Dateien muss man also darauf achten, nicht die ganze Datei in den Speicher zu laden. Funktionen wie file_get_contents() oder file() etc. fallen schonmal als Lösung raus. Des Rätsels Lösung sind Streams bzw. Streamwrapper. Wir öffnen die Datei mit Hilfe der Funktion fopen(), und können uns dann innerhalb der Datei bewegen (fseek()) bzw. Zeichen lesen (fgets() bzw. fgetc())

Doch wie finden wir die Punkte, an der die letzten Zeilen beginnen? Ganz einfach, wie fangen ganz hinten an und tasten uns zurück. Immer wenn wir auf ein \n stossen haben wir eine vollständige Zeile gefunden und speichern diese in einem Array. Das machen wir so lange bis wir die Anzahl der gewünschten Zeilen haben und können diese dann ausgeben.

Ein Stolperstein könnte noch entstehen aufgrund von restriktiven PHP-Einstellungen. Häufig untersagt man seinen Apache-VHosts, aus ihren Document-Roots auszubrechen und aktiviert noch weitere Sicherheitsmaßnahmen. Damit dieses Script funktioniert muss zumindestens die OpenBaseDir Einstellung angepasst werden, ansonsten kommt das Script nicht an die entsprechenden Verzeichnisse falls sie außerhalb der erlaubten Verzeichnisse liegen.

Hier das Script. Es hat nur eine sehr einfache Authentifizierung eingebaut, und auch die Ausgabe ist nicht W3C konform, aber es funktioniert in allen Browsern und das reicht mir. Außerdem werden <iframe>s benutzt, um die einzelnen Logdateien anzuzeigen. Wer mehr Zeit investieren möchte kann das ganze natürlich auch mittels <div>s und AJAX umbauen.

<?php
authenticate();

$files = array(
	'/var/log/messages' => 15,
	'/var/log/phperrors.log' => 15,
	'/var/log/syslog' => 15,
	'/var/log/mail.log' => 15,
	'/var/log/auth.log' => 10,
	'/var/kunden/logs/phpgangsta-error.log' => 5,
);

if (isset($_GET['logfile']) && isset($files[$_GET['logfile']])) {
	$lines = readLastLinesOfFile($_GET['logfile'], $files[$_GET['logfile']]); ?>
	<html>
		<head>
			<meta http-equiv="refresh" content="5">
		</head>
		<body style="margin: 0">
			<div style="font-size: 0.6em; white-space: nowrap">
				Filesize: <?=round(filesize($_GET['logfile'])/1024/1024,2) ?> MB<br/>
			<?	foreach ($lines as $line) {
					echo "<nowrap>$line</nowrap><br/>\n";
				} ?>
			</div>
		</body>
	</html>
<? } else { ?>
		<html>
			<head>
			</head>
			<body>
			<?	foreach($files as $filename => $lines) {
					echo $filename; ?>
					<iframe src="tail.php?logfile=<?=urlencode($filename)?>" style="height: <?=$lines*13+15?>px; width: 100%;"></iframe>
			<?	} ?>
			</body>
		</html>
<? }

function readLastLinesOfFile($filePath, $lines = 10) {
    //global $fsize;
    $handle = fopen($filePath, "r");
    if (!$handle) {
    	return array();
    }
    $linecounter = $lines;
    $pos = -2;
    $beginning = false;
    $text = array();
    while ($linecounter > 0) {
        $t = " ";
        while ($t != "\n") {
            if(fseek($handle, $pos, SEEK_END) == -1) {
                $beginning = true;
                break;
            }
            $t = fgetc($handle);
            $pos--;
        }
        $linecounter--;
        if ($beginning) {
            rewind($handle);
        }
        $text[$lines-$linecounter-1] = str_replace(array("\r", "\n", "\t"), '', fgets($handle));
        if ($beginning) break;
    }
    fclose($handle);
    return array_reverse($text);
}

function authenticate() {
	if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] != 'padmin' || $_SERVER['PHP_AUTH_PW'] != 'meinadminpwd') {
	    header('WWW-Authenticate: Basic realm="Mein Bereich!"');
	    header('HTTP/1.0 401 Unauthorized');
	    echo 'Tja, so nicht mein Freund!';
	    exit;
	}
} ?>

Sicherlich nicht der weltbeste Code, aber es erfüllt seit langer Zeit seinen Zweck und benötigt keine speziellen PHP-Extensions oder Betriebssysteme. Man könnte sich z.B. auch eine Lösung erstellen, wo mittels

system('tail -n 10 /var/log/syslog')

gearbeitet wird, aber das läuft eben nicht überall.

Sehr interessant sieht auch diese tail-Lösung mittels libevent unter PHP aus. Dazu muss man allerdings libevent und die PHP-extension libevent installieren, es läuft also nicht direkt auf jedem Server. Es dürfte allerdings deutlich performanter sein bei sehr großen Dateien würde ich tippen.

Written by Michael Kliewe

Februar 1st, 2010 at 9:32 am

Kein Hexenwerk: SMS/MMS mit PHP versenden

with 14 comments

Ziemlich kompliziert? Ziemlich teuer? Ich zeige euch das Gegenteil an einem kurzen Beispiel.

Ohne hier Werbung machen zu wollen werde ich in meinem Beispiel die Schnittstellen von www.sms77.de nutzen. Es gibt da draußen hunderte andere Anbieter, die vielleicht besser oder günstiger sind. Falls ihr einen Favoriten habt, nur her damit. Ich habe vor Jahren ein Konto bei SMS77 eingerichtet und nutze es hier und da für wichtige SMS-Benachrichtigungen.

(Nein, dies ist kein Werbeposting, ich bekomme (leider) kein Geld dafür.)

Also, um Nachrichten ins Mobilfunknetz verschicken zu können benötigt man einen Zugang zum Mobilfunknetz. Da man sich eine solche Umgebung nicht selbst aufbauen möchte, nutzt man sogenannte SMS-Gateways. Diese Anbieter haben eine Infrastruktur, um auf verschiedenen Wegen Nachrichten in das Mobilfunknetz zu leiten. Das sind neben SMS auch MMS, Logos, Klingeltöne und Wap-Push-Nachrichten.

Jedes SMS-Gateway bietet verschiedene Schnittstellen, wo wir die Nachrichten einliefern können. Die beliebteste ist wohl die HTTP-API, es gibt aber auch ein Email2SMS-Gateway, man kann die Nachrichten auch via Desktop-Anwendung versenden oder mit Hilfe eines Java-Programms, das man sich auf dem Handy installiert. Andere Provider bieten auch SS7, SMPP, XML etc an. Also mehr als genug Möglichkeiten, seine Information auf den Weg zu bringen.

Die ersten beiden Möglichkeiten möchte ich hier aus PHP heraus vorstellen. Wenn wir eine SMS versenden möchten, gibt es da noch verschiedene Qualitätsstufen und Typen. Dazu gibt es ganz unten im Artikel eine Übersicht.

Zuerst wollen wir eine einfache SMS versenden. Wir wählen den Typ BasicPlus und die HTTP-API:

$text = 'Hallo Admin, der Cronjob um 17 Uhr ist wider Erwarten nicht gelaufen. Bitte nachgucken!';
$url = 'http://sms77.de/gateway/?' .
		'u=' . urlencode('phpgangsta') .
		'&p=' . urlencode('md5passworthier') .
		'&to=00491771234567' .
		'&text=' . urlencode($text) .
		'&type=basicplus';
// Zur Vorsicht lieber ein @ davor, damit im Fehlerfall
// und falscher php-config die URL nicht publiziert wird
$response = @file_get_contents($url);
// nun noch $response auswerten und Rückgabecode prüfen
if ($response == '100') {
	echo 'Alles wunderbar, SMS ist versendet worden';
} else {
	echo 'Ein Fehler ist aufgetreten: '.$response;
}

Nun möchten wir eine Quality-SMS versenden. Diese soll zeitversetzt morgen früh um 3:00 versendet werden. Wir werden hier eine verschlüsselte Verbindung zu SMS77 aufbauen, einen Absender werden wir auch festlegen. Zu guter Letzt prüfen wir noch den Status der SMS:

$text = 'Hallo Admin, ab ins Bett, es ist 3 Uhr!';
$url = 'https://sms77.de/gateway/?' .
		'u=' . urlencode('phpgangsta') .
		'&p=' . urlencode('md5passworthier') .
		'&to=00491771234567' .
		'&text=' . urlencode($text) .
		'&type=quality' .
		'&status=1' .
		'&delay=' . strtotime('29.01.2010 3:00:00') .
		'&from=' . urlencode('PHPGangsta');
// Zur Vorsicht lieber ein @ davor, damit im Fehlerfall
// und falscher php-config die URL nicht publiziert wird
$response = @file_get_contents($url);
// nun noch $response auswerten und Rückgabecode prüfen
$responseData = explode("\n", $response);
if ($responseData[0] == '100') {
	echo 'Alles wunderbar, SMS ist versendet worden, die
			Msg-ID ist '.$responseData[1]."\n";
	sleep(10);
	$url = 'https://gateway.sms77.de/status.php?' .
		'u=' . urlencode('phpgangsta') .
		'&p=' . urlencode('md5passworthier') .
		'&msg_id=' . $responseData[1];
	$statusResponseData = @file_get_contents($url);
	$statusResponse = explode("\n", $statusResponseData);
	echo 'Status ist: ' . $statusResponse[0] .
			' um ' . date('d.m.Y H:i:s', $statusResponse[1]);

} else {
	echo 'Ein Fehler ist aufgetreten: '.$response;
}

Als Absender kann man eine Zeichenkette (bis 11 Zeichen) wählen oder eine Rufnummer. Des weiteren erhalten wir nun eine zweizeilige Antwort (durch status=1 ausgelöst), und zwar den ReturnCode und eine Message-ID, die wir dann zum Beispiel nutzen können, um den Status abzufragen.

Dies ist nur ein kurzer Beispielcode, in einem Produktivsystem sollte man das ganze natürlich noch etwas ausbauen, beispielsweise werden Verbindungsprobleme nicht abgefangen, und auch den Status sollte man in einer Schleife prüfen falls die SMS ein paar Sekunden länger benötigt.

Weitere Details findet man bei SMS77 in der API-Beschreibung. Darin stehen auch Einzelheiten zu Logos, Klingeltönen, MMS usw.

Nun noch schnell die Email-Schnittstelle an einem Beispiel erklärt:

include 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();

$mail = new Zend_Mail('UTF-8');
$mail->setFrom('absender@phpgangsta.de')
	->addTo('email2sms@sms77.de')
	->setSubject('Hey Admin, Deine Webseite ist tot!')
	->setBodyText('email2smsKey#00491771234567#basicplus');

$config = array(
	'auth' => 'login',
	'username' => 'smtpusername',
	'password' => 'smtppasswort'
);
$transport = new Zend_Mail_Transport_Smtp('smtp.provider.de', $config);
// alternativ kann natürlich auch die gute alte mail() Funktion genutzt werden
// $transport = new Zend_Mail_Transport_Sendmail();
$mail->send($transport);

Es ist also nur eine normale Email mit bestimmten Angaben im BodyText, und schon kommt die SMS Sekunden später auf dem Handy an.

Das sind nur die Basics, es gibt noch viele weitere interessante Möglichkeiten, wie beispielsweise Inbound-SMS (SMS-Empfang, 3 Keywords bekommt man kostenlos), SMS-Daueraufträge, FreeSMS, Adressbuch (Automatische Geburtstags-SMS, oder statt Nummer einfach den Namen angeben beim Versenden) und einigem mehr.

Die Preise bewegen sich derzeit bei 3,5 Cent für eine BasicPlus-SMS und 7,9 Cent für eine Quality-SMS. Preise für Festnetz-SMS, MMS etc. entnimmt man der Webseite von SMS77.

Einfach mal ausprobieren, es gibt keine Einrichtungsgebühr, kein Mindestumsatz oder sonstwas, 2 Euro draufladen und Spass haben!

—————–

SMS77 unterscheidet zwischen

  • BasicPlus SMS: SMS wird über innerdeutsche Routen verschickt, Absenderadresse ist die Gatewayadresse von SMS77. Der Empfänger kann antworten, die Antwort ist bei SMS77 einsehbar (also eine anonyme Kommunikation)
  • Quality SMS: Man kann eine beliebige Absender-Nummer angeben. Damit könnte man eine falsche Nummer angeben, oder jedoch seine richtige Nummer, damit die Antwort auf dem eigenen Handy ankommt.
  • Flash SMS: Die meisten neuen Handys unterstützten diesen Typ. Die SMS wird direkt auf dem Bildschirm angezeigt, sie muss nicht erst geöffnet werden.
  • Festnetz SMS: Die SMS wird ins Festnetz verschickt und dort dann vorgelesen.

Written by Michael Kliewe

Januar 28th, 2010 at 9:21 am

Posted in PHP

Tagged with , , ,

„Meinten Sie“: Eingaben verbessern mit levenshtein() und soundex()

with 13 comments

Wer kennt es nicht: Wenn man in ein Suchfeld einen Suchbegriff eingibt, sich dabei jedoch vertippt oder es keine Suchergebnisse gibt, bekommt man manchmal einen alternativen Suchenbegriff vorgeschlagen, der mehr Ergebnisse liefert oder den falsch eingegebenen Begriff berichtigt. „Meinten Sie vielleicht“ oder „Did you mean“.

Doch wie funktioniert das?

Am einfachsten erledigt man diese Aufgabe mit der PHP-Funktion simliar_text() . Dieser Funktion übergibt man 2 Strings und erhält einen Ähnlichkeitswert, der die Anzahl der gleichen Buchstaben beschreibt. Alternativ kann man auch einen Prozentwert erhalten. Problem bei dieser Funktion: Sie ist langsam, die Laufzeit ist kubisch (n³).

Die aktuellere Funktion heißt levenshtein() und berechnet die Levenshtein-Distanz. Diese Distanz ist die minimale Anzahl an Einfüge-, Lösch- und Tausch-Operationen, um einen String in einen anderen zu verwandeln. Die Laufzeit dieser Funktion ist quadratisch (n²), allerdings gibt es auch Probleme: Es werden nur Strings mit maximal 255 Zeichen unterstützt. Falls auch längere Strings verglichen werden sollen, empfehle ich die Kommentare auf der PHP-Seite, denn dort stehen Quelltexte zum Nachbauen der Levenshtein-Distanz in PHP ohne diese Beschränkung, dort steht auch die Berechnung der prozentualen Gleichheit. Man sollte sich allerdings im Klaren sein, dass PHP-Code natürlich langsamer ist als die native PHP-C-Funktion.

Die levenshtein()-Funktion ist übrigens case-sensitiv, man sollte also überlegen die Parameter vorher in Kleinbuchstaben umzuwandeln (strtolower()).

Um noch bessere Ergebnisse oder Performance zu erhalten kann man auch die folgenden Funktionen hinzuziehen:

  • soundex() – Berechnung der Laut-Ähnlichkeit eines Strings. Mit Hilfe dieser Funktion kann man also bestimmen, ob sich 2 Worte ähnlich anhören! Für die deutsche Sprache ist die Funktion nicht ideal, in den Kommentaren stehen Verbesserungen und Alternativen. Ausprobieren!
  • metaphone() – Tut das selbe wie soundex(), doch kennt diese Funktion die Besonderheiten der englischen Aussprache. Besser ist jedoch DoubleMetaPhone, zu finden in den Kommentaren (lieber die PECL-Extension nutzen als die PHP-Klasse, um Performance zu steigern).
  • Die MySQL-Funktionen SOUNDEX() und „SOUNDS LIKE„, mit denen man die Berechnung der Datenbank überlassen kann.

Tolle Möglichkeiten, seine Suche zu verbessern und „fuzzy-like“ zu machen. Einziges Problem ist die Performance.

Written by Michael Kliewe

Januar 21st, 2010 at 8:52 am