phpinfo mit Parameter

Was mir persönlich bisher ziemlich unbekannt war, ist die Tatsache, dass man phpinfo auch mit Parametern aufrufen kann.

Ruft man einfach

phpinfo();

auf, dann erscheint ganz normal die Übersicht über alle Features des Webservers. Allerdings ist es ja oft so, dass man aus diesem ganzen Wust von Informationen nur einen ganz kleinen Teil benötigt, zum Beispiel die Servervariablen. Dazu benutzt man den (optionalen) Parameter $what (der heißt wirklich so).

Um nur die Servervariablen zu bekommen, ruft man die Funktion einfach mit der richtigen benannten Konstante auf:

phpinfo(INFO_VARIABLES);

Hier mal eine Übersicht über alle Konstanten mit eigenen Erklärungen, die „Originaltexte“ könnt ihr unten beim Quellenlink nachlesen:

  • INFO_GENERAL
    Informationen über das System, build-config, stream-filter, usw.
  • INFO_CREDITS
    Die Danksagungen. In meinem Zend Community Server ist der bereich bis auf die Überschrift leer.
  • INFO_CONFIGURATION
    Die Konfiguration des aktuellen Systems. safe_mode on oder off, error_log Verzeichnis usw. findet sich hier.
  • INFO_MODULES
    Die aktivierten Module des Servers. bz2 an? curl enabled? gd Unterstützung mit dabei? Alles hier.
  • INFO_ENVIRONMENT
    Die Umgebungsvariablen des Servers; userprofile, os, path, usw.
  • INFO_VARIABLES
    Quasi das ganze $_Server-Array nochmal im phpinfo-theme.
  • INFO_LICENSE
    Die PHP Lizenz.
  • INFO_ALL
    Zeigt alle oben angegebenen Parameter an, die gleiche Ausgabe, wie ohne Parameter.

sehr gut ist auch, dass man diese Parameter miteinander verknüpfen kann, um mehr, aber doch noch nicht alle Informationen zu bekommen:

phpinfo(INFO_MODULES + INFO_VARIABLES);

Somit erhält man nur 2 Bereiche, in denen man die Informationen heraussuchen kann, die man benötigt.

Quelle: http://de2.php.net/manual/de/function.phpinfo.php

PHP Filter vs. Zend Framework am Fall einer Mailadressprüfung

Eine Mailadresse prüfen, dass muss jeder Entwickler mal in seinem Code machen. Dass dabei eine Menge schief gehen kann, davon kann sicherlich jeder PHP-Programmierer ein Lied singen. Gemacht werden muss es aber, nur wie? Ich stelle die PHP eigene Funktion filter_var() gegen die Filterfunktion des Zend Frameworks und schaue mal, welche der beiden Lösungen denn „Praxistaugliche“ Antworten erzeugt.

Für die PHP eigenen Funktionen lasse ich filter_var mit Filteroption FILTER_VALIDATE_EMAIL antreten. Zur einfachen Übersicht baue ich eine Funktion „isEMailValid“. Diese soll bei korrekter Mailadresse true zurückliefern, ansonsten false; also eher trivial.


function isEMailValid($mail)
{
return (filter_var($mail, FILTER_VALIDATE_EMAIL) !== FALSE);
}

Für die Frameworks tritt das Zend Framework an und speziell dessen Zend_Validate_EmailAddress Klasse. Diese besitzt eine Funktion isValid, die das gleiche Verhalten zeigt wie unsere eigene „isEMailValid“ Funktion.

Als Beispiel gebe ich 4 ähnliche Mailadressen an, nur die erste ist eine „echte“ Mailadresse, bei allen anderen fehlt was bzw. ist ein Teil unvollständig. Sicherlich prüft das ganze nicht alle möglichen Fälle von Mailadressen ab, aber es zeigt einen (sinngemäßen) Fall, der mir Probleme bereitete und mich nun zum Wechsel von meiner „filter_var plus regex“-Methode zum ZendFramework bewegen konnte.


function isEMailValid($mail)
{
return (filter_var($mail, FILTER_VALIDATE_EMAIL) !== FALSE);
}

$mail1 = 'max.muster-mann@eine.beispieldomain.com';
$mail2 = 'max.muster-mann@com';
$mail3 = 'max.muster-mann eine.beispieldomain.com';
$mail4 = 'max.muster-manneine.beispieldomain.com';

printf('php-filter:'."\n");
var_dump(isEMailValid($mail1));
var_dump(isEMailValid($mail2));
var_dump(isEMailValid($mail3));
var_dump(isEMailValid($mail4));

printf('zend:'."\n");
require_once 'Zend/Validate.php';
require_once 'Zend/Validate/EmailAddress.php';
$mailvalidator = new Zend_Validate_EmailAddress();
var_dump($mailvalidator->isValid($mail1));
var_dump($mailvalidator->isValid($mail2));
var_dump($mailvalidator->isValid($mail3));
var_dump($mailvalidator->isValid($mail4));

Die Ausgabe:


php-filter:
bool(true)
bool(true)
bool(false)
bool(false)
zend:
bool(true)
bool(false)
bool(false)
bool(false)

Wie man sieht, die „normale“ filter_var() Funktion von PHP schlägt fehl und meldet die zweite Mailadresse als korrekt, was nicht richtig ist.

Allein das zeigt mir nun, dass ich mich auf die Ergebnisse der PHP eigenen Prüfung nicht verlassen kann. Ich werde das ganze weiterhin beobachten, allerdings wird das nächste Projekt dann Funktionen aus dem Zend Framework benutzen. Das schöne ist, dass ich nur einmal in meiner Prüfungsmethode meiner Validatorklasse die Prüfung ändern muss, damit Projektweit die „richtige“ Prüfung benutzt wird. OOP ist toll 😉

PHP-Version: 5.2.13

Nachtrag vom 24.09.2010

Die ersten 5 Kommentare brachten mich auf andere Wege und zu mehr „Erkentnis“ des ganzen. Danke an IchBinIch und SkaveRat für die Hinweise!

Nimmt man noch ein paar andere Abwandlungen der Mailadressen zeigt sich ein Bild, dass man erklären sollte, denn filter_var und Zend arbeiten anscheinend völlig unterschiedlich. Oder doch nicht?

Was soll man nun benutzen? filter_var oder doch die Zend-Funktionen?

Die Antwortet lautet: Es kommt drauf an!
Je nachdem, was man mit der Prüfung erreichen will, benötigt man entweder das eine oder das andere. Hier die beiden Möglichkeiten:

  • 1. Prüfung für den Mailversand!
    Der wohl häufigste Fall. Man möchte wissen, ob eine Mailadresse dazu taugt, etwas per SMTP dort auch hinsenden zu können (Zugangsdaten, Newsletter, …). Hier lautet die Antwort: Zend-Framework, denn das prüft, ob ein String eine versendbare Mailadresse nach RFC 5321 beinhaltet (siehe auch Kommentar 3 von SkaveRat).
  • 2. Prüfung, ob es ganz generell eine Mailadresse sein könnte!
    Hier ist die Antwort: filter_var, denn diese Funktion prüft die generelle Syntax des Strings, nicht dessen Nutzbarkeit. Dabei können auch Global-Parts wie ‚localhost‘ eingesetzt werden, allerdings werden auch Mailadressen wie ‚a@com‘ durchgelassen, da diese ja theoretisch auch Mailadressen sind.

Man sieht, dass es durchaus auf den Anwendungsfall ankommt, will man sich für eine Filterklasse entscheiden. Ich werde das auf jeden Fall bei der nächsten Prüfung berücksichtigen.

Relative URL’s im tinyMCE

Das Problem: Jedesmal wenn ich im tinyMCE-Editor eine URL angebe, wird diese in eine „bessere“ Form gebracht.

Beispiel: Eine URL der Art „/bilder/meinBild.png“ wird zu „../bilder/meinBild.png“.

Die Browserengines sind zwar so schlau, dass ganze auch wirklich Problem- und Warnungslos anzuzeigen, aber Suchmaschinen sind da schon pingeliger.

Die Lösung lautet, die Option „convert_urls“ in der Konfiguration des Editor auf „false“ zu setzen.

tinyMCE.init({
...
convert_urls : false,
...
});

Damit bleibt dann der Eintrag so, wie dieser in den Editor geschrieben wird.

2 Jahre Blogjubiläum

Heute ist Jubiläum: Mein Blog – dieses Blog – gibt es nun genau 2 Jahre. Zumindest ist das erste Posting so alt. Es handelt vom Suchfeld des Firefox und wie man damit direkt bei php.net nach Funktionen usw. suchen kann. Seitdem kamen zuerst nur sporadisch neue Beiträge und ich musste mich auch erstmal an das „neue“ Medium Blog gewöhnen.

Mittlerweile habe ich das ganze besser im Griff, poste auch ab und zu mal was interessantes und ich hoffe, das ein oder andere Posting konnte euch auch mal helfen.

Zukünftig möchte ich natürlich noch mehr posten, vor allen Dingen natürlich mehr Code-Sachen. Schauen wir mal, wer mich ein wenig kennt, der weiß, dass ich wirklich nicht viel Zeit habe. Aber ich versuche, nicht ungeduldig werden 😉

So, jetzt habe ich euch genug von eurem Zeug abgehalten; los, los, wieder zurück an die Arbeit 😉

Memo an mich selbst #2

@Sascha: Alter siehst du gestresst aus!
Pass auf, ich schreib es dir nochmal hin:

  • Klassenmethoden und -attribute sind die mit dem static!
  • foreach in Java ist das hier: for (String item: stringList) {…}
  • Nicht vergessen: Erst den Datentyp bestimmen, dort die speziellste Methode finden und erst dann (!) überschreiben!
  • Vergiss die Sache mit den Threads, dafür ist die Zeit zu eng.
  • Die Sache mit den Objektatributen bei generischen Klassen wird noch geklärt!
  • Und noch ganz wichtig: Die Klausur ist am Samstag von 10-13 Uhr in Bochum!

Danke an mich selbst 😉

Mehrere MySQL-Tabellen mit ähnlichen Inhalten in einer Abfrage

Mehrere MySQL-Tabellen mit ähnlichen Inhalten in einer Abfrage abhandeln und das ganze dann auch noch am besten mit dem gleichen Code durchlaufen, obwohl in den Tabellen zwar ähnliche, aber nicht gleiche Daten stehen? Dazu noch eine Art „Flag“, dass man weiß, in welcher Zeile ein Ergebnis aus Tabelle1 und in welcher es aus Tabelle2 kommt? Kein Problem…

Sicherlich, man könnte nun die Tabellen einzeln abfragen und ebenso einzeln abhandeln, aber, hej, das kann jeder, wir, WIR, können das besser. WIR benutzen dazu nicht 2 oder x Abfragen, nur um hinterher zu sehen, dass wir alle Felder gleich behandeln, bis auf eins. WIR machen dann sowas wie hier 😉

Die Vorrausetzungen nochmal:

  • Ich brauche x Felder aus der Datenbank aus Y Tabellen.
  • Von diesen x Feldern sind x-1 Felder gleich, auch im späteren Code werden x-1 Felder völlig gleich abgearbeitet.
  • Die Spaltennamen in der Datenbank sind für diese X Felder nicht gleich, die Inhalte bzw. deren Signatur bzw. deren Datentypen aber schon.
  • Ich normalisiere die x Spalten aus den y Tabellen
  • Ich verkette diese y Tabellen miteinander nacheinander.

Zauberwort hierbei heißt „normalisieren“ und „verketten“. Zuerst funktioniert das ganze nur dann, wenn man zum einen die gleiche Anzahl Spalten hat und zum anderen die Spalten selbst auch den gleichen Typ und den gleichen Namen besitzen.

Gleiche Anzahl und (Daten)Typ, dass müsst _ihr_ sicherstellen; beim Namen kann man ja mit dem Zauberwort AS etwas „tricksen“ 😉

Trickreich wird es allerdings beim Punkt „wissen, aus welcher Tabelle das ganze kommt“, denn evtl. möchte oder muss man ja doch in der späteren Verarbeitung das eine oder andere Feld entsprechend unterschiedlich behandeln. Dazu gibt es diesen kleinen „Trick“. Man legt einen Spaltennamen für die Spalte fest, in der die Unterscheidung stattfinden soll und schreibt dann in Klammern und Hochkommata einen Bezeichner davor. Der Bezeichner gibt an, was in der Zeile steht.

Beispiel: tabelle1 enthält Produktdaten, tabelle2 die Kategorien, wir wollen u.a. die Produktnamen und die Kategorienamen auslesen und auch wissen, was wir nun haben. Als verkettung benutzen wir UNION bzw. UNION ALL. Die Unterscheidungsspalte nenne ich „herkunft“:

SELECT produkt_name AS name, ("tabelle1")herkunft, ...
FROM tabelle1
UNION ALL
SELECT kategorie_name AS name, ("tabelle2")herkunft, ...
FROM tabelle2
ORDER BY irgendwas

Das Ergebnis sieht dann in etwa so aus:

name herkunft
produkt 1 tabelle1
kategorie 1 tabelle2
kategorie 2 tabelle2
produkt 2 tabelle1
produkt 3 tabelle1
usw. usw.

Nun sehe ich sehr schön, woher die Daten stammen und kann somit die kleinen Unterschiede mittels einer Kondition in die richtigen Bahnen lenken.

Ich hoffe, ich konnte dem ein oder anderen helfen…

Eine Liste aller Seiten mit webEdition

Eine Liste über alle oder nur bestimmte Seiten auf seinem Webspace ist immer nützlich, z.B. als Sitemap, als Übersicht der neuesten 10 Seiten, für’s RSS Feed oder oder oder.
Der Ablauf, wie man das ganze mit webEdition realisiert, ist im Prinzip immer der gleiche. Das Zauberwort bzw. we-Tag, welches man dazu braucht, nennt sich we:listview.

Mit einer we:listview erhält – mit einer der vielen „type“-Attribute – auch alle Dokumente, die einem bestimmten Filter entsprechen. Einzig zwingendes Attribut bei we:listview ist das „type“-Attribut, welches für den aktuellen Fall auf „document“ gesetzt wird.

Eins vorweg: we:listview erfasst Dokumente nur unter zwei Bedingungen:
Entweder die Dokumente haben in den „Eigenschaften“ das Attribut „durchsuchbar“ gesetzt oder man setzt das Attribut searchable des we:listview auf „false“.

Durchsuchbar vs. searchable
searchable ist der einfachste Fall, dabei werden allerdings alle Dokumente erfasst, die das entsprechende Feld „durchsuchbar“ auf „False“ haben. Das die Dokumente dieses Attribut nicht gesetzt haben, dürfte der normalfall sein. Dageben findet „searchable=true“ nur Dokumente, die das entsprechende Feld auch gesetzt haben.
Nun das für und wieder:
Im Fall „searchable=false“ erfasse ich alle Dokumente und kann mit setzen der Eigenschaft „durchsuchbar“ ein Dokument von der Suche ausnehmen. Sinnvoll, wenn nur wenige Dokumente _nicht_ in der Suche erscheinen sollen.
Im anderen Fall kann ich exakt steuern, welche Dokumente gefunden werden dürfen.
Welches von beiden man wählt kommt auf den Anwendungsfall an.

Filter nach Typ
Will man nur bestimmte Dokumenttypen erfassen – die we:listview benennt Dokumente nicht nur nach webEdition Dokumenten, sondern auch Bilder usw.; ein we:listview Dokument ist etwas, was physikalisch auf dem Server liegt – dann muss man „contenttypes“ setzen. Im Falle von „neueste 10 Seiten“ oder „Sitemap“ setzt man dies auf „text/webedition“. Dann erfasst man alle Seiten, die man auch mit webEdition erstellt hat und bearbeiten kann.

Filtern nach Verzeichnissen
Manchmal möchte man nicht „alles zeigen“, sondern nur Seiten aus bestimmten Verzeichnissen. Dann gibt man die ID der Verzeichnisse einfach im Attribut „workspaceID“ als komma-separierte Liste an. Möchte man zusätzlich noch Unterverzeichnisse einbeziehen, dann muss das Attribut „recursive“ auf „true“ gesetzt werden.

Startpunkt und Anzahl der Links
Schön zum begrenzen der angezeigten Links: „rows“ und „offset“.
Mit „rows“ legt man fest, wieviele Treffer gezeigt werden, mit „offset“ bestimmt man … nunja, den offset halt. Diese Einstellungen werden für z.B. für eine Blätterfunktion genutzt, der interessierte Leser liest dazu bitte das Beispiel auf der Referenzseite an und sieht bei den Tags „we:listviewPageNr“, „we:listviewPages“, „we:next“, „we:ifNext“, „we:back“ und „we:ifBack“ nach.

Sortierreihenfolge und -richtung
Braucht man eine Sortierreihenfolge, dann setzt man „order“ auf einen dieser Werte:
random() -> Zufällige ausgabe
we_creationdate -> Erzeugungsdatum
we_filename -> Dateiname
we_id -> Die ID
we_published -> Wann veröffentlicht
we_moddate -> Zuletzt geändert

Normalerweise werden die Einträge aufsteigend sortiert, benötigt man absteigende Sortierung, setzt man „desc“ auf „true“ (z.B. für die neuesten Seiten wichtig).

Weiteres
we:lisview bietet noch viele weitere Optionen, die ich selbst bisher nicht gebraucht habe und für dieses Beispiel einfach zu viel sind. Ich bitte den geneigten Leser, mal in der Doku zu we:listview nach zu schlagen … auch wenn diese bisweilen nicht so ergiebig ist, wie man es sich oft wünschen würde.

we:repeat und we:field
Um durch die Treffer zu iterieren, benötigen wir das we-Tag we:repeat, dass dann von „offset“ bis „rows“ die Treffer der Reihe nach ausgibt. Die Informationen zu den Treffern findet man in we:field, über dessen Attribute kommt man dann an alles, was benötigt wird.

Ein Beispiel
Auf einer meiner Seiten gebe ich die 5 Dokumente aus, die ich mit webEdition veröffentlicht habe, allerdings dürfen diese nur in bestimmten Verzeichnissen liegen und müssen dazu noch das Attribut „durchsuchbar“ gesetzt haben. So kann ich ganz genau steuern, welches Dokument dort auftaucht und welches nicht. Dieser Ausschnitt hier ist 1:1 und liegt in einem Template namens „listviewtest.tpl“









Ich kann nun die Darstellung der 5 neuesten Dokumente einfach mittels



oder



in meine Vorlagen einbinden.

Für eine Sitemap sollte man das Attribut „rows“ evtl. weglassen, so dass alle Dokumente angezeigt werden, ebenso kann man sich natürlich was für Darstellung überlegen.

Das hier soll nur ein Einstieg sein und ich hoffe, ich konnte euch das „rüberbringen“. Die Erstellung von Listen auf Basis der webEdition Dokumente sollte nun kein großes Problem mehr sein.

PHP-Snippet: int2bin

Integer zu Binär casten:


function int2bin($number)
{
$number = filter_var($number,FILTER_SANITIZE_NUMBER_INT);
return ($number=='') ? FALSE : base_convert($number, 10, 2);
}

Eingabe: Eine Zahl oder ein String mit Zahlen darin.
Rückgabe: FALSE, wenn keine Zahl in $number gefunden wurde oder die entsprechende Binär-Ausgabe der Zahlen in §number.
Hinweis: $number=’A21′ erhält ‚10101‘ als Rückgabe!

Achtung: boolean filtern mit filter_var()

Wer seine Eingaben prüfen will, kommt um Filter nicht mehr rum. Ganz rudimentär und ohne „externe“ Frameworks kommt man aus, wenn man filter_var() benutzt.
Doch Achtung: Eine Validierung auf boolean in diesem Stil geht schief, denn:

filter_var('1', FILTER_VALIDATE_BOOLEAN);
filter_var('0', FILTER_VALIDATE_BOOLEAN);
filter_var('abc', FILTER_VALIDATE_BOOLEAN);

Folgende Ergebnisse:

1 -> true
2 -> false
3 -> false

Die ersten beiden Fälle sind richtig – obwohl man bei Zeile 2 auch ein true erwarten dürfte, denn die 0 ist ein boolean-Wert und damit könnte die Prüfung auch true sein, wenn man mit der Absicht prüft, ob es denn überhaupt ein boolean-Wert ist oder nicht, nicht welcher boolean-Wert vorliegt. Das klärt sich aber gleich – , der dritte Fall ist nicht gewollt oder nach der o.a. Logik ist diese Zeile richtig und Zeile 2 falsch, je nach Ansicht.

Klar ist aber jedem, dass man damit nicht arbeiten kann. Für ein Ergebnis, mit dem man arbeiten kann, muss man eine Option in filter_var() mit dem dritten Parameter setzen.

filter_var('1', FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE);
filter_var('0', FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE);
filter_var('abc', FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE);

Ergebnisse nun:

1 -> true
2 -> false
3 -> null

Und damit kann man nun sehr gut arbeiten…