PHPUnit mit Netbeans unter Cygwin

Eine kleine Anleitung und ein paar Scripts um die in Netbeans integrierte PHPUnit-GUI unter Cygwin zum Laufen zu bekommen.

Software:

  • Windows 7
  • Cygwin
  • NetBeans 7.3.1
  • PHP 5.4.11
  • PHPUnit 3.7.21

Ich gehe davon aus, dass Windows, Cygwin (c:\cygwin) und NetBeans bereits installiert sind.

PHP ist momentan nicht im Haupt-Repository von Cygwin enthalten. Es lässt sich aber leicht über apt-cyg installieren.
Wurde apt-cyg installiert (siehe Projekt-Seite für eine Anleitung), kann es verwendet werden um über Cygwinports PHP zu installieren.

apt-cyg -m ftp://ftp.cygwinports.org/pub/cygwinports/ install php

Cygwinports bietet auch eine Reihe von PHP-Erweiterungen an. Hier gibt es eine Liste aller verfügbaren Pakete.

Jetzt sollte der Befehl php auf der Shell verfügbar sein.

Kleine Anmerkung zwischendurch: Ich verwende PHP unter Cygwin übrigens auch im Apache, der auch über Cygwin installiert wurde, und setze nicht mehr auf Distributionen wie XAMPP und co.

Als nächstes installieren wir PHPUnit als PHAR-Datei:

mkdir /usr/share/phpunit
cd /usr/share/phpunit
wget http://pear.phpunit.de/get/phpunit.phar
chmod +x phpunit.phar

# Optional, aber praktisch
ln -s /usr/share/phpunit/phpunit.phar /usr/bin/phpunit

Wurde der letzte optionale Befehl ausgeführt, könnte man seine PHPUnit-Testcases schon jetzt auf der Shell mit dem Befehl phpunit ausführen.

Da wir aber die GUI von NetBeans verwenden wollen, müssen ein paar Dinge beachtet und ein Problem umgangen werden.

Zunächst müssen wir für NetBeans, das ja unter Windows läuft, eine bat-Datei unter /usr/share/phpunit/phpunit.bat anlegen:

@echo off
c:\cygwin\bin\php.exe /usr/share/phpunit/phpunit_cygwin %*

Nicht vergessen, die Datei als ausführbar zu setzen:

chmod +x /usr/share/phpunit/phpunit.bat

Wie zu sehen ist rufen wir über PHP eine Script-Datei phpunit_cygwin auf. Diese Datei ist entscheidend. Sie dient als Wrapper um die Pfadangaben zwischen Windows (NetBeans) und Linux (Cygwin) zu konvertieren.

Die Datei /usr/share/phpunit/phpunit_cygwin enthält folgenden Inhalt:

#!/usr/bin/php
<?php

array_shift($argv);

array_walk($argv, function(&$a){

    if(!preg_match('/(.*)([a-z]:\\\\.+)/i', $a, $matches)){
        return;
    }

    $a = $matches[1].'"'.exec('/usr/bin/cygpath -a "'.$matches[2].'"').'"';

});

$args = implode(' ', $argv);

system('/usr/bin/php /usr/share/phpunit/phpunit.phar '.$args);

Es bietet sich an auch diese Datei ausführbar zu setzen:

chmod +x /usr/share/phpunit/phpunit_cygwin

Was passiert hier? NetBeans arbeitet und kennt unter Windows nur das DOS-/Windows-Pfadformat. Da PHP und damit auch PHPUnit unter Cygwin (Linux) ausgeführt werden, müssen die Pfade in ihr entsprechendes Cygwin-Gegenstück konvertiert werden.

Hierzu liefert Cygwin bereits ein Tool namens cygpath mit.
Mein PHP-Script kümmert sich darum, dass jeder übergebene Parameter in das UNIX-Pfadformat konvertiert wird, falls der Parameter auch wirklich einen Windows-Pfad enthält.

Danach wird PHPUnit mit den korrigierten Parametern aufgerufen.

Abschließend müssen wir noch NetBeans einrichten:

PHPUnit-Konfiguration unter NetBeans

Das Ergebnis zeigen folgende Screenshots:

Test-Ergebnis

Ausgabe von PHPUnit

PHP: Aktionen im Hintergrund ausführen

Hi,

ich habe heute etwas an der Performance meines Systems geschraubt.

Vor allem der integrierte PHP-Piwik-Tracker sorgte hier für messbar längere Ladezeiten meiner Seiten, da dieser natürlich erst eine Verbindung zum Piwik-Server aufbauen muss etc.

Meine Idee war also die HTML-Ausgaben an den Client zu schicken, danach die Verbindung zum Client zu trennen und anschließend erst den Seitenaufruf auf dem Server zu tracken, ohne dass der Client etwas davon mitbekommt.

Meine endgültige, funktionierende Lösung:

<?php

// Output-Buffering starten
ob_start();

// Seiteninhalt
echo '<html>... stuff ...</html>';

register_shutdown_function(function(){

	// Client kann Vorgang nicht abbrechen
	ignore_user_abort(true);
	
	// Verbindung schließen
	header('Connection: close');
	header('Content-Length: '.ob_get_length());
	
	// Alle Buffer löschen und ausgeben
	while(@ob_end_flush());
	
	// Daten an den Browser schicken
	flush();
	
	// Besuch tracken
	Piwik::track();
	
	// Session beenden
	session_write_close();
	
});

Geholfen hat mir: http://stackoverflow.com/questions/138374/close-a-connection-early

E-Mail-Validierung für IDN-Domains in HTML5, PHP und Javascript

Tach,

ich habe mich heute mal (wieder) informiert, wie man am besten E-Mail-Adressen auf korrektes Format validiert.

Die Situation

Bisher habe ich in PHP immer den mit Version 5.2.0 eingeführten E-Mail-Filter und in Javascript einen regulären Ausdruck verwendet:

PHP:

function checkEmail($email){
	return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

Javascript:

function checkEmail(email){
	return email.match(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i);
}

HTML5:

Auch HTML5 bietet die Möglichkeit, ein Eingabefeld des Typs E-Mail zu erzeugen:

<!DOCTYPE html>
<html lang="de">
    <head>
        <title>E-Mail-Validierung</title>
    </head>
    <body>   
        <form>
            <input type="email">
            <input type="submit">
        </form>   
    </body>
</html>

Demo für HTML5 und Javascript.

Das Problem

Das Problem ist nun, dass keine der drei Lösungen IDN-Domains bei E-Mail-Adressen akzeptiert (siehe [1] und [2]). Das heißt Adressen wie foo@bär.de, die durchaus gültig sind, werden als ungültig erkannt.

Die Lösung

Als Lösung muss man also den Domain-Teil in Punycode konvertieren, um danach das Format prüfen zu können.

PHP:

Für PHP kann man hierzu auf das Modul intl bzw. speziell auf die Funktion idn_to_ascii
zurückgreifen.

function checkEmail($email){
	$parts = explode('@', $email);
	return count($parts) == 2 && filter_var($parts[0].'@'.idn_to_ascii($parts[1]), FILTER_VALIDATE_EMAIL) !== false;
}

Javascript:

Für Javascript bin ich auf eine tolle Bibliothek von Mathias Bynens gestoßen. Damit lässt sich das Problem analog zu PHP lösen.

function checkEmail(email){
	var parts = email.split('@');
	if(parts.length != 2){
		return false;
	}
	var converted = parts[0] + '@' + punycode.toASCII(parts[1]);

	return converted.match(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i);
}

Das Fazit

Mit etwas mehr Aufwand ist es in PHP und Javascript also durchaus möglich IDN-E-Mail-Adressen korrekt zu erkennen. Meiner Meinung nach sollten die PHP-Entwickler ihren E-Mail-Filter erweitern, damit er IDN-Domains akzeptiert.

Für HTML5 gibt es dafür keine Lösung, solange die Browser-Entwickler es nicht berücksichtigen. Eingabefelder vom Typ „url“ können übrigens mit IDN-Domains umgehen. Warum also nicht auch E-Mail-Felder?

Da davon auszugehen ist, dass sich „Umlautdomains“ (wie man so schön sagt) in Zukunft weiter verbreiten werden, wäre es wichtig, dass Browser jetzt schon im vollen Umfang darauf vorbereitet wären.

[1] http://www.php.net/manual/en/filter.filters.validate.php#102398
[2] http://barrow.io/posts/email-validation-of-double-byte-domains/

RSS von Twitter mit OAuth abholen

Hi,

Twitter hat ja am 31. August die Basic Auth abgeschaltet. Es wird nur noch OAuth unterstützt.

Blöd, wenn man Twitter über den RSS-Feed gelesen hat, denn RSS-Reader wie Google Reader unterstützen kein OAuth.

Ich hab mir also ein kleines PHP-Script geschrieben, das mich per OAuth authentifiziert, meinen RSS-Feed von Twitter abholt und ausspuckt. Dieses Script kann man dann einfach in den RSS-Reader einbinden.
Hierfür habe ich die schlanke TwitterOAuth-Lib von Abraham Williams benutzt.

Bevor man das Script benutzen kann, muss man erstmal eine App bei Twitter registrieren.
Das geht unter folgender Adresse: http://dev.twitter.com/apps/

1) Zunächst muss man eine neue App anlegen, indem man rechts auf „Register a new app“ klickt.

2) Bei den Einstellungen sind 2 Punkte wichtig.

  • Application Type auf Client setzen
  • Default Access type auf Read-only setzen

3) Danach landet man wieder auf der Übersicht seiner Apps. Einfach die gerade neu erstellte App auswählen

Für das Script brauchen wir nun 4 Token/Keys:

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret

4) Auf der Detailansicht der App findet man den Consumer Key und das Consumer Secret.

5) Dann rechts auf „My Access token“ klicken.

6) Hier findet man dann den Access Token und das Access Token Secret.

Diese 4 Werte trägt man nun einfach oben in der index.php ein und fertig.

Download: Twitter OAuth RSS Feed Fetcher

In PHP prüfen ob ein Array assoziativ ist

Ich wollte gerade prüfen ob ein Array assoziativ ist oder nicht. Hier ein paar Funktionen, die ich dazu gefunden habe.

function isAssoc($arr){
   return count( array_diff( array_keys($arr), array_keys( array_keys( $arr )) ));
}
function isAssoc($arr){
    return array_keys($arr) !== range(0, count($arr) - 1);
}
function isAssoc($arr){
    return array_values($arr) !== $arr;
}

PHP – Quick and Dirty

Hi,

jetzt habe ich aber schon lange nichts mehr gepostet. Zeit wirds!
Bin gerade zufällig auf einen kleinen aber feinen PHP Hack gestoßen.

Kurz gefasst: Hier eine schnelle Methode um anonyme Objekte zu erzeugen.

< ?php

$obj = (object) array('foo' => 'bar', 'property' => 'value');

echo $obj->foo; // prints 'bar'
echo $obj->property; // prints 'value'

?>

PHP, Vererbung und __sleep()

Hi,

heute bin ich mal wieder auf einen interessanten und zugleich ärgerlichen Bug gestoßen. Versucht man Eigenschaften einer Klasse zu serialisieren, die in der Vater-Klasse als private deklariert sind, spielt PHP leider nicht mit.

Zumindest dann nicht, wenn man die Methode __sleep() implementiert. Lässt man diese weg, liefert PHP das erwartete Ergebnis zurück.

Der „Fehler“ ist schon lange bekannt, aber leider immer noch nicht gefixt. Stattdessen verweisen die Entwickler auf das Interface Serializable und behaupten, dies sei kein Bug

http://bugs.php.net/bug.php?id=36172

Ich finde, das ist sehr wohl ein Bug…

Es gibt noch einen weiteren Workaround für dieses Problem, den man in den Kommentaren in der Dokumentation findet.

http://de2.php.net/manual/en/language.oop5.magic.php#81492

class A {
  private $foo;

  function __sleep(){
    return array("\0A\0foo");
  }
}

class B extends A {
}

// Liefert nun das richtige Ergebnis
unserialize(serialize(new B()));

Somit lassen sich auch private Eigenschaften der Vaterklasse serialisieren.

PHP: Array-Benchmark

Hiho,

heute hab ich mich ein bisschen mit Arrays in PHP beschäftigt. Speziell ging es mir darum herauszufinden, welche Methode am schnellsten ist um einen neuen Wert ans Ende eines Arrays zu speichern.

Dabei habe ich 4 Möglichkeiten untersucht:

  1. Der []-Operator
    $a = array();
    $a[] = "foobar";
    
  2. Die Funktion array_push()
    $a = array();
    array_push($a,"foobar");
    
  3. Via Index und count()
    $a = array();
    $a[count($a)] = "foobar";
    
  4. Direkt via Index
    (setzt voraus, dass der nächste Index bekannt ist)

    $a = array();
    $a[0] = "foobar";
    

Ich habe in meinem Benchmark 10.000 mal einen 1000 Byte-Wert an das Array angehängt. Was dabei herausgekommen ist, zeigt dieses Diagramm.
(Achtung: logarithmische x-Achse!)

PHP Array Benchmark

Fazit:

  • Der Gewinner ist mit knappem Vorsprung Methode 1, der []-Operator.
  • Methode 4 liefert einen nahezu identischen Wert, da ja der selbe Operator benutzt wird.
  • Den 3. Platz hat sich array_push() gesichert. Das Geschwindigkeitsdefizit gegenüber Methode 1 lässt sich durch den zusätzlichen Overhead erklären, da hier bei jedem Durchlauf eine Funktion aufgerufen wird.
  • Und, nicht überraschend, weit abgelegen die Kombination aus count() und Index-Zugriff. Der Grund ist offensichtlich. Das wiederholte Aufrufen der count()-Methode, die das Array vermutlich jedes mal mit einem Aufwand von O(n) durchläuft um die Größe/Länge zu ermittlen, erzeugt eine deutliche Zeitverzögerung.
  • Eine kleine Vermutung meinerseits: Arrays sind in PHP als Liste implementiert, was das Mischen von assoziativen und numerischen Arrays erleichert, allerdings anscheinend auch das Zählen der Elemente verlangsamt. Ich würde mich aber wundern, wenn die interne Datenstruktur des PHP-Arrays keine Zähl-Variable enthielte, welche die aktuelle Größte speichert. Mein Ergebnis spricht nicht dafür. Weiß es jemand genau?

Das wars auch schon mit meinem kleinen Benchmark. Eventuell werde ich in Zukunft noch ein paar weitere durchführen. Hat jemand Vorschläge?