PHP5.3: Designüberlegungen

Nun ist schon etwas Zeit ins Land gestrichen und manch einer mag sich vielleicht gefragt haben, wieso ich PHP5.3 noch nicht erwähnt habe, wo ich doch vorher in der Beta Phase gerne mal einen Beitrag darüber verfasst habe. Die größte und am meisten bejubelte Neuerung mag ich nun garnicht weiter erläutern, denn zum Beispiel Blackflash hat dazu 3 Links gesammelt, die das schon ausreichend beleuchten. Das Manual tut sein Übriges.

Überhaupt: So euphorisch vor dem Release die Namensräume gefeiert wurden, so ruhig wurde es doch plötzlich danach. Die Frage ist nun auch nicht wie man damit etwas macht. Die Frage nun lautet, wie man damit irgendwas besser, als vorher umsetzen kann.

Herantasten

Mal zwei vielleicht etwas ungewöhnliche Gedankenansätze um sich an das Problem heran zu tasten:
In Prag war es früher so, dass es keine Hausnummer und Straßennamen gab, sondern jedes Haus hatte einen eigenen Namen, womit sich auch solch nette Kneipennamen wie “Die zwei Kätzchen” erklären. Das ist nur bis zu einem Grenzwert praktikabel, aber das Vorgehen findet sich auch heute noch als Altlast in Programmiersprachen wieder, wie strcspn() in PHP, oder aton() in C. Tja, auch damals war man schreibfaul.

Zweites Beispiel: In Berlin-Lichtenberg gibt es eine Ecke, in der sind alle Straßen nach amerikanischen Seen benannt, was beim “Huronsee” (eigentlich “Lake Huron”) zumindest ein Schmunzeln verursacht — es klingt auf Deutsch doch etwas komisch, wenn man das “o” verschleift. Nagut, das soll ja auch nur ein Beispiel sein, denn nimmt man es genau, findet man sowas in jeder Stadt mehr als ein mal, das ist sogar mehr die Regel, als die Ausnahme. Also begann man dort schon Straßennamen systematisch zu ordnen. Auch das findet sich in PHP zB bei den Array- oder String-Funktionen wieder.

Zum Punkt

Die Einleitung ist vorbei, zurück zu PHP. Zu was fehlende “explizite Gruppierung” in der Vergangenheit geführt hat, sollte jeder schon mal gesehen haben. Die WordPress wp_*()-Funktionen sind dabei noch harmlos, zu beanstanden wär hier, dass daneben auch Funktionen wie user_trailingslashit() auftauchen, die sich nicht an das Muster halten. Das Zend Frameworksche Zend_A_B_C_D_E sieht dagegen schon spannender aus, dafür hält es sich strikt an ihre Konvention. Bei langen Klassenbezeichner gibt aber auch mein Widescreen auf.

Namespaces spannen nun Bäume auf, die einmal für kürzere Bezeichner sorgen, indem sie durch Alias abgekürzt werden können, andererseits sorgen sie “rein aus Versehen” gleich auch für eine strikte und konsequente semantische Gruppierung: Die Bezeichner der Funktionen und Klassen sind nun nicht mehr für den Präfix (hier: Namespace) verantwortlich. Im Grunde war es das schon. Theoretisch könnte man nun jeden Unterstrich “_” durch ein Namespace-Separator “\” ersetzen. Das mag für existierende Projekte auch zweckmässig, wenn nicht sogar zwingend notwendig sein, will man denn den kompletten Rewrite vermeiden. Nehmen wir wieder das ZF würde das dort bedeuten, dass ein Namespace ein Verzeichnis entspricht. Bei neuen Projekten könnte man allerdings auf die Idee kommen die alten Dogma zu überdenken.

Von der Schlange[1] lernen

Unibedingt war ich gezwungen mich mit Python auseinander zu setzen. Wer sich erstmal durch die Einstiegshürden geschlagen hat, wird grad die List-Comprehension zu schätzen wissen, das nur so nebenbei. Ohne jetzt eine Sprache zu loben, oder zu kritisieren lohnt sich doch auch immer ein Blick zur Seite. Das Prinzip hinter Python:

Modul my.module
Datei my/module.py
Paket my.package
Verzeichnis my/package/

Dabei kann es kein Modul und Paket mit gleichem Namen geben, denn in der Notation unterscheiden sie sich nicht und wären für den Parser demnach auch nicht unterscheidbar: Findet er ein Modul mit dem Namen, ist die Sache für ihn geklärt. Es fällt allerdings auf, dass sich diese Struktur 1:1 auf PHP übertragen lässt. Statt path.to.my.module heißt es dann \path\to\my\module und eine Dateistruktur gibt PHP sowieso nicht vor, weswegen man diese auch übernehmen kann.

Der nächste Schritt wäre der import. In Python

import my.module as m

in PHP

use my\module as m;

Die Ähnlichkeit ist schon verblüffend, wobei Python noch über ein paar mächtigere Ergänzungen verfügt. So ist es in Python möglich nur einzelne Teile (Klassen, Funktionen, ..) des anderen Namespace in den Aktuellen zu holen, was zumindest bei Klassen in PHP auch funktioniert, allerdings für jede Klasse einzeln definiert werden muss. Besonders hervorheben möchte ich noch von Python, dass Variablen im prozeduralen Code auch nur im entsprechenden Raum gültig sind, aber explizit in fremde Räume importierbar sind. Dafür Neulings-Bonus von PHP: Hängt man an den Bezeichner noch ein “\” und nennt es “Paket”, kann es Pakete und Module gleichen Namens geben, denn jetzt sind sie unterscheidbar.

In Python ist man damit bereits einsatzfähig, denn durch die fixe Dateistruktur ist auch gleich ein eingebauter Autoloader drin. Für PHP erhoffe ich mir an dieser Stelle eine Mechanismus ähnlich dem SPL Autoloader, womit sich dann das Verhalten simulieren lässt. Aber auch ohne lässt sich mit einer einfachen Funktion und dann eben ein Funktionsaufruf pro use-Zeile Ähnliches erreichen:

namespace my;
function import ($module) { // No reserved keyword
  // PATH ist eine Namespace-Konstante
  $file = \my\PATH . DIRECORY_SEPARATOR . str_replace('\\'. DIRECTORY_SEPARATOR, $module) . '.php';
  return require $file;
}

// andere Datei
namespace my\real\cool\space;
use my\other\space as bla;
import \my\import('my\\other\\space');

Wo wär dabei sind wär dann noch ein Autoloader für (Namespace-)Konstanten und -Funktionen für mich noch das höchste der Gefühle

Alte Bekannte

Der Code-Schnippsel offenbart schon ein paar andere Details. Als da wäre als Erstes, dass es eine Funktion ist. Den OOP-Zwang einiger Frameworks (zB ZF), ebenso wie die OOP-Ablehnung (wie zB bei WordPress [2]) muss man nicht zwangsläufig unterstützen, beide können schön Koexistieren. Die Fixierung auf Klassen und Objekte stört mich allerdings etwas mehr. Mal als Beispiel: Wozu muss man eine statische factory()-Methode haben? Betrachtet man sich so eine Methode, stellt man häufig fest, dass sie nur von den Eingabeparameter abhängt, was so ziemlich der Definition einer Funktion entspricht.

Punkt 2: Da ist eine Konstante ausserhalb einer Klasse. Auch Konstanten litten unter der Präfix-Grippe, mit Namespaces werden sie wieder handzahm, vorallen wenn sie dann noch (vorwiegend) im eigenen Namespace verwendet werden. Auch anders herum kann ein Schuh draus werden: Darüber kann ein Namespace Informationen über sich verteilen. Triviales Beispiel: Kostante VERSION.

return require $x;?!? Ja, auch Skripte selbst können Rückgabewerte haben, wenn sie im prozeduralen Code mit return $y; abschließen. Das mag in der Vergangenheit in Vergessenheit geraten sein. Tatsächlich ist es sogar so, dass ein Include standardmässig 1 liefert, wenn es kein return gibt. Wofür das sinnvoll ist, muss erst wieder entdeckt werden, vorallen weil es mindestens eine Zeile prozeduralen Codes erfordert, was den ein oder anderen Java-Jünger eventuell zuwider läuft

Etwas ungünstig ist wohl die Wahl des Namespace-Trennzeichens getroffen; In Strings siehts .. merkwürdig aus. Allerdings uferte die Diskussion bei den Machern wohl schon zu Beginn der Entwicklung aus. Es gibt echt schlimmeres ;) . Selbst bei kurzer “Raucherpause-Überlegungen” fiel mir kein passender Ersatz ein.

“Proof of Concept” (“In Progress”)

Ich würde hier nicht lang und breit erzählen, wenn ich nicht selbst nach “der Antwort” suchen würde: Was nun? Zur Zeit erteste ich die genannten Konzepte in einer Art “Proof of Concept”, zusammen mit einigen anderen, kleineren Ergänzungen. Eine davon ist zum Beispiel den Einsatz von Closures bzw invokeable Objects, die am Ende irgendwo das Selbe sind [3]. Das Prinzip ist in PHP eigentlich auch nicht neu [4], bloss selten umgesetzt und recht unhandlich, und eigentlich auf was völlig anderes, was ich wohl auch nochmal am Rande erklären werde. Was sich damit anstellen lässt, beschreibe ich ebenso später, hier auch wieder im Vergleich zu Python ;) .

Ein anderer Punkt nicht-systematischer Natur wäre der Grad der Abstraktion. Gefühlt behandelt man diesen gerade bei PHP immer noch etwas stiefmütterlich. Ein (unscharfes) Beispiel: Beim ZF gibt es immer noch wahre 2000-Zeilen-pro-Klasse Monster, die weder erben, noch ein Interface implementieren. Was heißt das für den Entwickler? Möchte er die Funktionalität erweitern, ändern, oder erzeugen, muss er entweder die Klasse erweitern und die komplexen Zusammenhänge der Methoden untereinander verstehen, oder aber eine eigene Klasse schreiben, die die ganzen Methoden implementiert, ohne zu wissen, ob diese überhaupt notwendig sind. Hinzu kommt bei letzterem, dass ein Type-Hint womöglich die ursprüngliche Klasse als Oberklasse erzwingt, was wiederum den ersten Fall erzwingt, wobei wieder alle Methoden überschrieben werden müssen. Für sowas hat man eigentlich die Vererbung nicht erfunden, ganz zu schweigen von so netten Pattern das Decorator Pattern.

Klassischer Abschied

Habt Spass dran!

[1] Keine Kommentare dazu: Ich weiß, dass der Namensvetter eigentlich “Monty Python” war.
[2] Es gibt Klassen in WordPress (SMTP), die aber noch in PHP4 definiert sind und deshalb getrost als “veraltet” betrachtet werden kann.
[3] Bei Definition einer Closure wird daraus zur Laufzeit ein Objekt der Klasse Closure, die selbst wiederum __invoke() implementiert.
[4] Ehemals bekannt unter “Lambda” über create_function, bzw callable.


Bad Behavior has blocked 87 access attempts in the last 7 days.