Willkommen beim PHP Bloke

Ja. Wirklich. Noch ein Blog über PHP, HTML, CSS, MySQL, JavaScript, das Web und so weiter. Diesmal von mir. In erster Linie hilft schreiben ja auch dem Denkprozess, aber wenn die Artikel dann schonmal da sind...

PHP zu JavaScript Compiler

22. Mai 2017 um 14:19 Uhr von Wolfgang Stengel zu Programmierung, PHP, JavaScript
Seit ein paar Jahren arbeite ich "on and off" an einem Compiler der PHP in JavaScript umwandelt. Etwas Ähnliches gibt es mit Niklas von Herztens php.js schon, meine Engine ist jedoch keine VM sondern ein in PHP geschriebener Parser und Compiler. Während das resultierende JavaScript immer noch eine kleine Runtime (~50 K komprimiert) benötigt, findet die Hauptarbeit in PHP statt.

Hier ist eine kleine Online-Demo. (Ausgaben werden in die Browser-Konsole geschrieben.)

Die meisten im alltäglichen Gebrauch wichtigen Features sowie eine ganze Reihe von spezielleren Eigenschaften von PHP (Referenzen, Iteratoren, Output Buffering, Anonyme Funktionen, Magic Methods, ArrayAccess u.v.m.) sind bereits umgesetzt und der Compiler besteht auch den Großteil der PHP-nativen Unit-Tests (sofern relevant). Zu den größsten fehlenden Bereichen gehören das Dateisystem, Traits und Generators. Wozu das Ganze? Gute Frage. Für mich war es hauptsächlich eine Fingerübung für JavaScript-Programmierung und der Reiz am Detailverständnis der Hintergründe von PHP. Zugegebenermassen motivierte mich auch die zunehmende Fragmentierung in der modernen Webentwicklung (HTML, CSS, PHP, MySQL, JavaScript, Client/Server-Interaktion).

Destruktoren und Session-Daten in der Datenbank

04. Dezember 2016 um 13:02 Uhr von Wolfgang Stengel zu PHP, MySQL
Eines meiner Projekte verwendet einen eigenen Session-Handler um die Session-Daten in die Datenbank zu schreiben anstatt in's Dateisystem. Das Einhängen und der Aufruf eigener Funktionen mit session_set_save_handler() ist dafür der erste Schritt.

Jedoch trat hierbei das Problem auf dass PHP zum Zeitpunkt des Aufrufs der Write-Funktion bereits die meisten Objekte im Speicher zerstört hatte, inklusive des Objekts welches ich für den Datenbankzugriff verwende. Hierbei handelt es sich um ein bekanntes und dokumentiertes Problem. Die PHP-Dokumentation schlägt vor, den Write-Handler manuell zu triggern mit folgender Zeile:

register_shutdown_function('session_write_close');
Shutdown-Funktionen werden vor dem Zerstören der Objekte aufgerufen. Dadurch ist mein Datenbankobjekt zum Zeitpunkt des Schliessens der Session noch vorhanden und die Daten können in die Datenbank gespeichert werden.

Das nächste Problem war dann jedoch dass die Shutdown-Funktionen auch vor den Destruktoren aufgerufen werden. Was passiert also wenn ein Destruktor eines Objekts Daten in der Session verändert? In meinem Beispiel war es ein Warenkorbobjekt eines Shop-Systems. Der Warenkorb sollte seinen Inhalt am Ende des Scripts in der Session speichern. Durch das Verwenden des register_shutdown_function()-Tricks wird der Warenkorb jedoch erst in die Session geschrieben nachdem diese bereits über session_write_close() geschlossen wurde.

Daher habe ich mir folgenden Trick ausgedacht:

class SessionShutdownHelper
{
    function __destruct() { session_write_close(); }
}
register_shutdown_function(function()
{
    $one=new SessionShutdownHelper();
    $one->one=$one;
});
Hier wird session_write_close() nicht direkt über eine Shutdown-Funktion aufgerufen, sondern indirekt über einen Destruktor eines ansonsten leeren Objekts. Durch die zirkuläre Referenz wird der PHP-Interpreter gezwungen das SessionShutdownHelper-Objekt bis ganz zum Ende des Scripts im Speicher zu halten, weil solche Kombinationen erst durch die Cycles-Collection entfernt werden können. Dadurch wird also session_write_close() erst nach allen anderen Destruktoren aufgerufen, und mein Warenkorb landet sicher in der Datenbank.

Null, Undefined und NaN in JavaScript

14. August 2016 um 16:22 Uhr von Wolfgang Stengel zu JavaScript
JavaScript hat ein paar aussergewöhnliche Variablen-Zustände, die, wenn man JavaScript nur ab und zu einsetzt, einem immer wieder Kopfzerbrechen bereiten. Man möchte nur kurz einen Wert im DOM ändern oder eine Plausibilitätsprüfung einbauen, wird aber von dem lustigen kleinen gelben Ausrufezeichen links unten im Browser geärgert. Hier eine kurze Zusammenfassung wie man null, undefined und NaN ausseinanderhält und zuverlässig darauf prüft.

Der Wert null wird relativ selten von JavaScript-Operationen selbst erzeugt. Er kann jedoch dazu verwendet werden einen "leeren" Zustand einer Variablen zu markieren. Darauf lässt sich leicht mit dem Operator === prüfen:
var leer=null;
if (leer===null) { ... }
Der Wert undefined schlägt einem schon öfter entgegen, unter anderem wenn eine Variable nicht deklariert ist, ein Funktionsparameter nicht übergeben wird oder eine nicht existierende Objekt-Eigenschaft abgefragt wird. Kurz gesagt ist undefined das Pendant zu NULL in PHP. Zuverlässig prüfen lässt sich darauf, ohne einen Laufzeitfehler zu erzeugen, nur mit typeof():
if (typeof(unbekanntevariable)=="undefined") { ... }
Wenn es um Objekteigenschaften oder bereits sicher deklarierte Variablen geht, funktioniert auch der direkte Vergleich (ohne Quotes):
var ob={};
if (ob.unbekannteeigenschaft==undefined) { ... }
var leer;
if (leer==undefined) { xxx }
Damit lassen sich auch optionale Funktionsparameter mit Defaultwert erzeugen:
function alertNumber(number)
{
    if (number==undefined) number=0; // Gibt 0 aus wenn Argument nicht gegeben wurde
    alert(number);
}
In der ersten Variante würde if (unbekanntevariable==undefined) { ... } nicht funktionieren, da bereits vor dem Vergleich, nämlich beim Auswerten von unbekanntevariable, ein Laufzeitfehler ausgelöst wird.

Den Wert NaN (Not a Number) spuckt JavaScript immer dann aus wenn ein Wert, der keine Zahl ist, in einem numerischen Zusammenhang verwendet wird. Es gibt zwar die Funktion isNaN(), diese prüft aber nicht nur auf den konkreten Wert NaN, sondern gibt auch true für z.B. isNaN("foo") zurück. Auf den konkreten Wert NaN lässt sich nur mit einem Trick prüfen:
var num=parseInt("Hallo");
if (typeof(num)=="number" && num+""=="NaN") { ... }
In verschiedenen Foren findet man immer wieder Tipps auf null, undefined oder NaN mit einem simplen if (variable) { ... } zu prüfen, was aber z.B. bei einem Integer mit Wert 0 oder einem Leerstring nicht funktioniert.
PHP Wolfgang Stengel