PHP5 – dynamische Variable – superglobale Arrays

Gestern bin ich an einem kleinen aber interessanten Problem in der PHP-Programmierung hängen geblieben. Manchmal gibt es Situationen, in denen man mit einer dynamisch erzeugten Variablen – also einer Variablen, deren Bezeichnung erst zur Laufzeit festgelegt wird – eine Aufgabe schnell in den Griff bekommt. In meinem Fall versuchte ich, in einem Objekt eine dynamische Variable zu verwenden, die sich situationsbedingt entweder auf das _GET oder das _POST – Array beziehen sollte. Dies misslang zunächst – das Thema ist daher vielleicht auch für andere interessant. Es geht also um die Kombination von dynamischen Variablen mit superglobalen Arrays. Der Reihe nach:

Voraussetzung – Erzeugung von dynamischen Variablen

Einfache dynamische Variable
erzeugt man über folgenden Mechanismus

$dyn = “anracon”;
$$dyn = “anracona”;
echo ” \$dyn hat den Wert: ” . $dyn . “<br>”;
echo ” \$anracon hat den Wert: ” . $anracon . ” ( = identisch mit \$\$dyn : ” . $$dyn . ” ) <br>”;

Dynamische Array-Variable
sind etwas anders zu behandeln. Man beachte die geschweiften Klammern:

$dyna = “ay_anracon”;
${$dyna}[0] = “anracona”;
echo “\$dyna hat den Wert: ” . $dyna . “<br>”;
echo “\$ay_anracon[0] hat den Wert: ” . $ay_anracon[0] . ” ( = identisch mit \${\$dyna}[0] : ” . ${$dyna}[0] . ” ) <br>”;

(Das Ganze geht natürlich auch mit assoziativen Arrays).

Hinweis: Man könnte bei der Erzeugung dynamischer Variable zur Not auch mit der “eval”-Funktion arbeiten – das ist aber wirklich umständlich, total unsicher und wir lassen das der Übersichtlichkeit halber einfach weg.

Dynamische Bezugnahme auf “Superglobale Arrays”

Man übergibt einem Programm die GET-Variable “snr” – Bsp.: get_parameter.php5?snr=100

Enthält das Programm auf oberster Ebene (also nicht in einer Subfunktion oder einer Methode eines Objekts) folgenden Code:

$b = true;
$ind = ‘snr’;
echo ” get_parameter = ” . $_GET[$ind] . “<br>”;
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
echo ” get_parameter = ” . ${$sgv}[$ind] .”<br>” ;

so erhält man zwei identische Outputzeilen mit dem Inhalt “getparameter = 100”. Die Bezugnahme der dynamischen Array-Variablen ${$sgv} auf das “_GET”-Array funktioniert also wie erwartet.

Bezugnahme auf “Superglobale Arrays” in Objektmethoden

Hat man im Programm eine Klasse und ein zugehöriges Objekt definiert, so kann man mit einer Objektmethode direkt auf das GET-Array zugreifen, da es superglobal ist und sein Geltungsbereich daher nicht mit dem “global”-Statement spezifiziert werden muss. Folgendes funktioniert also innerhalb des Programms “get_parameter.php5”:

class GetParam
{
var $gp;
function __construct()
{
$this->gp = $_GET[‘snr’];
}
}
$Ob = new GetParam;
echo ” get_parameter = ” . $Ob->gp . “<br>”;

Dynamische Bezugnahme auf “Superglobale Arrays” in Objektmethoden

Folgendes geht jedoch nicht – trotz superglobaler Gültigkeit des “_GET”-Arrays:

class GetPar
{
var $gp;
function __construct()
{
$b = true; // Nur als Beispiel
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
$this->gp = ${$sgv}[‘snr’];
}
}
$Obj = new GetPar;
echo ” get_parameter = ” . $Obj->gp . “<br>”;

Denkt man etwas darüber nach, so kommt man auf folgende Vermutung: Woher soll der Interpreter wissen, dass er eine globale Variable vorsehen muss? Er wird sich schon aus Sicherheitsgründen vielmehr auf einen lokalen Gültigkeitsbereich der dynamischen Variablen zurückziehen. Dann sollte aber Folgendes helfen:

class GetP
{
var $gp;
function __construct()
{
$b = true; // Nur als Beispiel
if($b) { $sgv = “_GET”; }
else { $sgv = “_POST”; }
//
global ${$sgv};
//
$this->gp = ${$sgv}[‘snr’];
}
}
$GP = new GetP;
echo ” get_parameter = ” . $GP->gp . “<br>”;

Tatsächlich erhält man nun den gewünschten Output. Das “global”-Statement ist zwar wirklich nicht schön. Aber bei einer Bezugnahme auf ein superglobales Array kann man diese stilistische Unsauberkeit vielleicht verzeihen.

Abschließende Anmerkung

Ziel dieses Beitrags war es, darauf hinzuweisen, dass man innerhalb von Objektmethoden mit Hilfe dynamischer Variablen nicht ohne weiteres auf superglobale Arrays zugreifen kann. Scheut man das erforderliche “global” – Statement, so muss man seien Lösungen von vornherein anders konzipieren – was in der Regel sicher möglich ist.

(In meinem Fall wollte ich zu Prüfzwecken aus dem Objekt heraus einen direkten (!) Zugriff auf die Arrays ohne Zwischenvariablen gewährleisten und trotzdem die Wahl zwischen _GET und _POST haben.)

FPDF und PHP – erste Erfahrungen

Ich habe vor kurzem die Aufgabe erhalten, mit PHP PDF-Dokumente zu generieren, die auf den Einträgen in einer Datenbank basieren. Möglichst kostengünstig. Der Griff zur PFDlib war wegen der relativ hohen Kosten für kommerzielle Zwecke also nicht möglich. Zudem behagte mir die PDFlib auch nicht, da sie als Modul im Webserver (Apache) integriert werden muss. Das wäre bei dem Web-Service-Provider, der die Domaine meines Kunden hostet, dann eher ein Problem geworden.

In meiner Not habe ich dann zur Opensource Bibliothek “FPDF” (http://www.fpdf.de/) gegriffen. Die dortigen (objektorientierten) Bibliotheken benötigen nur PHP und werden nicht als Apache-Module geladen. Ich muss nach ein paar Tagen produktiver Arbeit feststellen, dass ich mit der Bibliothek sehr positive Erfahrungen gemacht habe:

1) Die Installation ist sehr einfach.
2) Die Doku ist knapp bemessen, aber verständlich und letztlich ausreichend. Ein Blick in die Online-Tutorials mit den dortigen Beispielen ist sehr hilfreich.
3) Die Einbindung und Erweiterung der zu nutzenden Bibliotheks-Objekte in eigene PHP-Objekte und Methoden ist auf bequeme Art und Weise möglich.
4) Die Methodenaufrufe zur Gestaltung des PDF-Dokumentes sind zwar begrenzt, dafür aber sehr einfach zu verstehen und anzuwenden.
5) Footer und Header-Methoden sind einfach für die eigenen Zwecke zu überschreiben.
6) Tabellarische Inhalte lassen sich sehr bequem erstellen.
7) Die Geschwindigkeit bei der PDF-Erzeugung ist zumindest für einfache Zwecke ausreichend.
8) Es wird nicht zuviel versprochen – die angebotenen Methoden funktionieren.

Etwas umständlich ist die Einbindung von Truetype-Schriften und die zugehörige Erzeugung der metrischen Informationsdateien. Hier dem User etwas kompaktere Tools anzubieten, wäre nicht schlecht.

Aber alles in allem: eine empfehlenswerte Bibliothek für Leute, die PDF-Dokumente mit PHP erzeugen wollen und nicht zur PDFlib greifen können.

Skalierbare Web-Seiten unter Firefox 2

Mehrere unserer Webkunden haben explizit den Wunsch geäußert, das man Ihre Webseiten auch unter Firefox 2.0 skalierbar gestalten möge. Diesen Wunsch haben wir erfüllt. Es gibt aber einige Fallstricke, die wir hier kurz diskutieren wollen. Die Ausführungen sind vielleicht aber auch von generellem Interesse für diejenigen, die mit “em” als Größeneinheit statt “px” arbeiten.

Grundlagen
Die einheitliche (!) Skalierbarkeit aller Objekte einer Webseite im Firefox (vor Vers. 3.0) kann man durch zwei Dinge gewährleisten:

  1. Definition einer Standardschriftgröße im BODY-Tag nach dem Muster <body style=”font-family:…..; font-size:10px;”>
  2. Bemaßung aller Objektgrößen in den zugehörigen Style- oder Klassendefinitionen statt mit “px” mit “em”. Bsp.:
    <div style=”width:10.0em; height:20.0em;”>

Der Vorteil der Standardisierung auf die Schriftgröße 10px im Body-Tag ist eine vereinfachte Umrechnung:
Die “em”-Werte x 10 entsprechen in der nichtskalierten Seitenversion gerade dem herkömmliche Wert der Ausdehnung in Pixel.

Fallstrick 1: Sukzessive Mehrfachskalierung vermeiden!
Gibt man in einem Tag nicht nur die Geometriegrößen, sondern auch eine abweichende Fontgröße vor, z.B.
<div style=”position:absolute; width:6.0em; height:8.0em; font-size:1.2em; “>,
so wird das DIV keineswegs mit der Größe 60×80 dargestellt, sondern um einen Faktor 1.2 vergrößert. Es zählt immer die aktuelle Fontgröße, die für ein Tag gesetzt ist!

Gegenmittel: Um diesem Problem zu entgehen, kann man die Texte in <span>-Tags einbauen, für die man per CSS vorgefertigte Schriftgrößen definiert. Bsp:

<div style=”position:absolute; width:6.0em; height:8.0em; font-size:1.2em; “><span style=”font-size:12px;”>das ist der text im DIV<span></div>

Schwieriger ist das bei Formularelementen – hier muss man wirklich vorab Berechnungen zur resultierenden Größe anstellen. Formularelemente unterliegen aber ohnehin browserspezifischen Einschränkungen bzgl. der Darstellung. Ein Test in jedem Zielbrowser ist daher angebracht.

Fallstrick 2: Die minimale Schriftgröße im Browser
Hat der Anwender eine minimale Schriftgröße für seinen Browser eingestellt und unterschreitet (!) die im BODY definierte Standardschriftgröße die minimale Schriftgröße, so wird bei einer Seite, die konsequent mit “em”-Größeneinheiten designed wurde, die Geometrie insgesamt mit dem Faktor
“Minimale Schriftgröße des Browsers” / “Standardschriftgröße im BODY”
heraufskaliert.

Beachte: Selbst wenn die Schriftgröße im BODY nicht unter die minimale Schriftgröße des Firefox fällt, so kann doch für einzelne Tags eine andere kleinere Fontgröße definiert sein (s. Fallstrick 1). Für diese Tags wirkt sich dann die minimale Schriftgröße separat aus. Hat man das Vorgehen zu Fallstrick 1 nicht beachtet, so führt dies evtl. zu Schwierigkeiten bei der spezifischen Tagdarstellung oder gar der Seitendarstellung.

Fallstrick 3: Integrierte Flash-Movies
Typischerweise integriert man Flash-Movies W3C-konform mit SWFObjects (Version 1.5 oder 2.0) und verwendet ein zu Container-DIV. In der Version 2.0 wird der Container ersetzt, dessen Bemaßung wird aber übernommen). Hier ist es wichtig, dass der SWF-Film mit der Höhen- und Breiten-Angabe “100%” integriert wird. Dann werden die Maße des Containers beachtet – und wenn diese in “em”-Einheiten vorgegeben sind, so skaliert das SWF-Movie mit.

Wir wünschen viel Spaß beim Ausprobieren!

Eine Seite, die nach diesen Prinzipien von uns entworfen wurde, ist z.B.
http://www.med-aktiv.de

IE7, bicubic interpolation – nicht immer!

Im Internet kann man feststellen, dass viele Microsoft Fans begeistetr darauf reagiert haben, dass der IE7 mit Opera bzgl. der Skalierung von Webseiten und Ihrer Inhalte gleichgezogen hat. Ein Schlagwort in diesem Zusammenhang ist die bikubische Interpolation – für Numeriker und Physiker ein uralter Hut, mit dem jetzt halt auch Browser ausstaffiert werden.

Dagegen gibt es an und für sich nichts zu sagen. In der Praxis kann man aber das Verfahren der bikubischen Interpolation nicht immer “blind” einsetzen, und leider ist die Implementierung von MS auf den zweiten Blick auch nicht intelligent genug. Ein schönes Beispiel hatten wir vor kurzem, als wir auf einer Webseite Bilder aneinander gereiht haben, die vom Bildinhalt her nahtlos ineinander laufen. Realisierung z.B. mit Container-DIV und gefloateten Images. Nun möchte man diese Nahtlosigkeit gerne auch im Browser sehen – und dort auch nach einer Skalierung des gesamten Seite (Strg +).

Ergebnis in Opera: Kein Problem.
Ergebnis im IE7: In der Normalansicht (keine Skalierung) kein Problem. Bei einer Vergrößerung der Browseransicht mit “Strg +” wird aber offenbar jedes Bild einzeln bikubisch interpoliert. Dies führt im Ergebnis zu klar erkennbaren Rändern und manchmal sogar transparenten Zwischenräumen zwischen den Bildern. Das Hindurchschimmern des Hintergrundes kann man durch “margin:-1px” künstlich beheben. Die durch die Interpolation erzeugten minimalen Randstreifen jedes Bildes verschwinden jedoch nicht.
Ganz schlimm wird die Sachen, wenn die Bilder in einzelnen aneinander grenzenden Tabellenzellen untergebracht werden – obwohl die Bilder die Zellen ausfülllen, cellspacing, cellpadding, border auf 0 bzw. im Falle von CSS “border-collapse” etc. richtig gesetzt sind.

Das wird z.B. dann sehr unangenehm, wenn man Bilder verwendet, um den Rand eines Bereichs schön zu gestalten – z.B. mit abgerundeten Ecken. Die auftretenden Streifen zwischen den Bildern trüben die mühsam erarbeiteten Effekte sehr.

Woran liegt das? Nun, MS interpoliert die Bildern anscheinend einzeln. Bikubische Interpolationen sind an (scharfen) Rändern problematisch (Überschwingen). Hier muss man mit der Vorgabe der Randwerte aufpassen und den Rand ggf. einer Sonderbehandlung unterziehen, oder in der Nähe des Randes das Interpolationsverfahren wechseln. In der Regel der Vergleich mit Umgebungsdaten außerhalb des Randes. Im MS IE7 wird das leider nicht automatisch gemacht.

Es gibt aber eine Lösung:
Microsoft hat mit der Einführung der bikubischen Interpolation dem Anwender die Möglichkeit gelassen, per CSS-Anweisung auch ein anderes Interpolationsverfahren zu wählen. So gibt es zwei mögliche MS-spezifische CSS-Anweisungen:

-ms-interpolation-mode:bicubic
oder
-ms-interpolation-mode:nearest-neighbor

Bei Bildern, die man also auch nach einem Strg + nahtlos und ohne Randeffekte aneinandergehängt sehen möchte, muss man dem -Tag der aneinandergrenzenden Bilder halt ein
style=”-ms-interpolation-mode:nearest-neighbor” mitgeben. Dann funktioniert die Sache – leider auf Kosten der Interpolationsqualität.

Weboutput von PHP in Datei umlenken

Problemstellung
In einem unserer letzten Projekte stellte sich das Problem, den Output eines PHP-Programms – eine Webseite – nicht per HTTP zu einem Browser zu transportieren, sondern parallel auch als Datei wegzuschreiben. Die Webseite wurde auf Basis eines TPL-Templates erzeugt.

Lösungsansatz
Nun könnte man meinen, dass man hierzu in komplizierter Weise neben den Befehlen zum Füllen des Templates parallel fwrite-Befehle absetzen muss. Es geht jedoch viel kompakter und einfacher, indem man den Output puffert und die gepufferten Strings in die gewünschte Datei schreibt.

Diese Lösung haben wir ”’Jani Hartikainen”’ zu verdanken und in seinem Blog gefunden.

Lösung
Zu benutzen sind die Funktionen ob_start() und ob_end_flush(). Die Funktion ob_start() akzeptiert einen optionalen Parameter für eine callback-Funktion und hier setzt das Verfahren an.

Wir zeigen das an einem einfachen Beispiel, nämlich den letzten Zeilen des Codes zum Füllen eines ITX-Templates


function generate output () 
{
  // letzter Schritt im template, z.B. Anzeige von Warnungen und Fehlern    

  $template->setCurrentBlock("ERROR_VA");
     $template->setVariable("ERR_VIS", $vis_error); 				
     $template->setVariable("ERROR_MSG", $error_msg); 			
  $template->parseCurrentBlock();

  // Öffnen eines Files 
  $ob_file = fopen('output.html','w');

  // Pufferung unter Rückgriff auf die callback-Funktion aktivieren 
  ob_start('ob_file_callback');
		
  // Der Output - nämlich in diesem Fall die aus dem Template HTML-Datei wird in den Puffer gelenkt 
  $template->show();

  // Pufferung beenden und File schließen 		
  ob_end_flush();
  fclose($ob_file);
  exit;
}

function ob_file_callback($buffer)
{

  global $ob_file;                       // hier kann man über das Erstellen einer Klasse viel verbessern 
  fwrite($ob_file, $buffer);        // Umlenken des Puffers in die Datei output.html

  // Das nachfolgende Return-Statement sorgt dafür, dass der Output gleichzeitig auch auf dem Browser-Schirm  zu sehen ist. 
  return $buffer;

}

Verbesserungen
Dieses Beispiel verdeutlich nur das Prinzip. Jani Hartikainen beschreibt in seinem Blog-Artikel, wie man das Vorgehen in einer Klasse kapselt. Der geschätzte Leser möge sich dort bitte weiter informieren.