PHP - DIe Serverseite

PHP wird serverseitig und somit als Erstes ausgeführt. Der Ordner „root“ wird mit der Methode scandir() ausgelesen. Diese gibt einen Array mit den Inhalten des Verzeichnisses zurück. Um sicher zugehen, dass später beim Aufruf der Seite eine zufällige Anordnung passiert, wird bereits hier mit der Funktion shuffle() eine zufällige Reihenfolge der Array-Elemente programmiert.

Dieser Array $retval wird nun mit einer foreach-Schleife durchlaufen und von jedem Element im Array wird das Dateiformat bestimmt. Dabei hilft die Methode pathinfo()

 

$ext = pathinfo($value, PATHINFO_EXTENSION);

 

Nun kann mit einem if-else Statement abgefragt werden worum es sich bei jedem einzelnen Element in der Schleife handelt.

 

if ($ext == "jpg" || $ext == "jpeg" || $ext == "gif" || $ext == "png") { … }

 

Wenn es sich also um eine Bilddatei handelt, dann soll eine Miniatur des Bildes ausgegeben werden. Dazu benützen wir die print-Methode. Sie dient dazu um aus PHP Variablen oder Strings auf die HTML-Bühne zu „drucken“.

 

print "<... class='thumb' src='" . PATH . "/" . $value . "'/>";

 

$value ist dabei das entsprechende Element in der Schleife. PATH gibt den absoluten Pfad zur Datei an.

Selbiges passiert mit allen anderen Dateiformaten. Hierbei kann jedes beliebige Dateiformat abgefragt werden und somit könnte diese Mikro-Seite auf weitere Formate ausgebaut werden.

Wenn es sich beim gerade durchlaufenen Element um eine Textdatei handelt, wird deren Inhalt ausgelesen und in einem Div-Container auf der HTML-Bühne platziert.

 

$lines = file_get_contents(PATH . '/' . $value);
$lines = str_replace("\n","¶",$lines);
print "" . nl2br($lines) . "";

 

Mit der Methode file_get_contents() kann mit wenig Mühe der Inhalt (ein String) der Text-Datei abgefragt werden. Dieser String wird der Variable $lines zugewiesen. Aus kosmetischen Gründen kann hier noch mit str_replace() jeder Zeilenumbruch in ein Paragraphen-Symbol verwandelt werden. nl2br() wandelt PHP „\n“ Umbrüche in echte HTML Umbrüche um.

Um auch mit Ordnern arbeiten zu können, kann jedes Schleifen-Element auch ganz leicht auf „kein Dateiformat“ abgefragt werden.

 

else if ($ext == "") {

 

Wenn also kein Format existiert handelt es sich zwangsläufig um ein Verzeichnis.
Wenn dem so ist, wird wieder mit print ein Hyperlink mit der absoluten Pfadangabe zum Subverzeichnis ausgegeben.

Dazu bringen wir eine neue Konstante QUERY ins Spiel. Query dient dazu, um dem Link ein ?p= vor die eigentliche Pfadangabe zu hängen. Somit kann am Anfang des Scripts mit if(isset($_GET['p'])){ erfragt werden in welchem Verzeichnis sich der User gerade befindet.

 

<... href='" . QUERY  . PATH . "/" .$value."'> 

 

wird zu ?p=root/pfadangabe/aktuelle-Position-in-der-Schleife.format

 

Breadcrumbs - Brotkrümel
Um Besuchern der Webseite eine bessere Übersicht über einzelne Ordner und Dateisysteme des Portfolios zu gewähren, bauen wir noch eine Brotkrümel-Navigation ein. So kann im Portfolio leicht vor und zurück navigiert werden.

 

Übersicht

$crumb = explode("/", PATH);
if (PATH != 'root' && realpath(PATH)) {
print "…div class='breadcrumbs'>";
$newpath = '';
foreach($crumb as $index => $value) {
$newpath .= $value;

if($index < count($crumb)-1)
print "…a href='" . QUERY . $newpath ."'>$value > ";
// last item //
else
print $value;
$newpath .= '/';
}
print "…div>";
}
 

 

 

Die Variable $crumb stellt dabei einen Array dar, der jeden einzelnen Pfadabschnitt unseres Dateisystems beinhaltet. Mit der Methode explode() kann hier leicht unsere Pfadkonstante PATH bei jedem „/“ getrennt werden und als Element in den Array $crumb gegeben werden.

 

$crumb = explode("/", PATH);

 

Um die einzelnen Elemente wiederum auszugeben, durchlaufen wir den Array mit Hilfe einer foreach-Schleife. Um sicher zu gehen, dass jeder Brotkrümel-link auch auf den richtigen Pfad verweist, behelfen wir uns hier mit einer weiteren Variable $newpath.
Ihr wird pro Schleifendurchlauf der aktuelle Schleifenwert zu-addiert. Somit wächst der String in dieser Variable pro Schleifendurchlauf um das aktuelle Element und beinhaltet somit immer den aktuellen Gesamt-pfad.

 

$newpath .= $value; // .= Addition/Zuweisung

 

Um den User nicht zusätzlich zu verwirren und die Brotkrümelnavigation zu perfektionieren soll der letzte "Krümel" keinen Link darstellen und auch keinen weiteren > (Pfeil nach rechts) anzeigen. Der letzte "Krümel" zeigt ja das aktuelle Verzeichnis an und soll somit nicht auf sich selbst verlinken.

 

Die foreach-Schleife bietet eine sehr praktische Möglichkeit die aktuelle Schleifenzahl als Schlüssel direkt in eine Variable zu speichern.

 

 

foreach($crumb as $index => $value) {

 

Es wird also der Array $crumb durchlaufen, die aktuelle Schleifennummer wird in die Variable $index gespeichert und der Wert des aktuellen Schleifenelements wird in die Variable $value gespeichert. $index trägt nun bei jedem Schleifendurchlauf den aktuellen Schleifenwert und somit kann auf einfach Weise erörtert werden ob es sich um ein beliebiges Element in der Schleife handelt oder um das Letzte.

 

 

if($index < count($crumb)-1)

 

Die Methode count  gibt die Anzahl an Arrayelementen in der Variable $crumb zurück. Solange die $index Variable also kleiner ist als die Gesamtzahl an Elementen weniger eins soll jeder Brotkrümel als Link zum jeweiligen Verzeichnis ausgegeben werden. Trifft diese Abfrage nicht zu handelt es sich um das letzte Element und es wird kein Link ausgegeben.

 

 

 

print "<... href='" . QUERY . $newpath ."'>$value > ";

 

Aus kosmetischen Gründen kann dieser gesamte Codeblock in ein if-Statement gepackt werden, das sicherstellt, dass das root-Verzeichnis (in unserem Fall /root) nicht ausgegeben wird.

 

if (PATH != 'root' && realpath(PATH)) {

 

Also nur wenn der User in ein Subverzeichnis klickt werden die Brotkrümel ausgegeben.

 

Die Variable/Konstante QUERY
Die Variable QUERY hält den sogenannten query-string ?p= (p ist variabel, steht für Pfad) der es uns ermöglicht mit der Funktion isset() festzustellen, ob eine URL einen solchen query-string enthält.

http://en.wikipedia.org/wiki/Query_string
http://php.net/manual/en/function.isset.php

An den Anfang unseres PHP Scripts stellen wir nun folgende Zeilen:

 

 

	if(isset($_GET['p'])) {
if (preg_match('#(\./|\.\./|~/|\\\)#', $_GET['p']) || !realpath($_GET['p'])) {
print "<…div class='message'>";
print "directory is forbidden!";
print "<…div>";
} else if ($_GET['p'] == '') {
define(PATH, "root");
} else {
define(PATH, $_GET['p']);
}
} else { define(PATH, "root"); }
//
define(QUERY, "?p=");

 

Mit if(isset($_GET['p'])){ überprüfen wir als Erstes ob überhaupt ein ?p= in der URL vorhanden ist. Wenn dem so ist, speichern wir diesen Wert in die Konstante PATH.
Aus Sicherheitsgründen können wir es neugierigen Besuchern noch erschweren in der Ordnerhierarchie nach „oben“ zu wandern. Wie wir wissen sorgt ein einfaches ../ in einer Pfadangabe dafür, ein Verzeichnis darüber zu wählen. Um möglichst allen Möglichkeiten eines "hacks" auszustellen ist hier ratsam mit sogenannten Regular Expressions zu arbeiten. Regular Expressions oder kurz regex bieten die Möglichkeit Zeichenketten anhand bestimmter Regeln zu filtern. Da viele dieser regex-Ausdrücke sehr kompliziert erscheinen ist es ratsam im Internet nach vorgefertigten Regeln und Ausdrücken zu suchen und diese nach Möglichkeit abzuwandeln oder direkt zu verwenden. In unserem Fall ist der reguläre Ausdruck noch recht einfach nachvollziehbar.

Mit Hilfe der Methode preg_match kann in PHP ein regex auf einen String angewendet werden.

 

 

if (preg_match('#(\./|\.\./|~/|\\\)#', $_GET['p'])

 

Das erste Argument der preg_match Methode beschreibt den Suchstring (also den regulären Ausdruck) und das zweite Argument steht für das zu-durchsuchende Element. In unserem Fall soll der Pfad $_GET['p'])durchsucht werden auf #(\./|\.\./|~/|\\\)#

Wie in den meisten bekannten Script und Programmiersprachen beschreibt der Operator |  ein oder. Der Punkt . ist ein reserviertes Zeichen innerhalb von Regular Expressions. Wenn nach einem Punkt innerhalb eines Strings gesucht werden soll muss das reservierte Zeichen "escaped" werden. Dazu wird in PHP und in Regular Expressions generell ein Backslash \ verwendet. 

#(\./|\.\./|~/|\\\)# kann in seine Bestandteile zerlegt werden und so besser verstanden werden.

#(\./|\.\./|~/|\\\)# es wird überprüft ob die Pfadangabe die Zeichenfolge ./ oder ../ oder  ~/ oder \\\ enthält.

 

Wenn preg_match  true zurück gibt wird, also einer der überprüften Strings in der Pfadangabe gefunden wird soll eine Fehlermeldung für den User ausgegeben werden.

 

 

print "<…div class='message'>";
print "directory is forbidden!";
print "<…div>";

 

Wenn keiner der Zeichenketten gefunden wurde wird unsere Konstante PATH auf den Pfad in der URL-Angabe gesetzt.

 

define(PATH, $_GET['p']);

 

Wenn generell kein ?p= in der URL gefunden wird, also kein Query String gesetzt ist wird die Konstante PATH auf "root" gesetz.

 

 

Im Überblick

 

…?php

if(isset($_GET['p'])) {
if (preg_match('#(\./|\.\./|~/|\\\)#', $_GET['p']) || !realpath($_GET['p'])) {
print "…div class='message'>";
print "directory is forbidden!";
print "…/div>";
} else if ($_GET['p'] == '') {
define(PATH, "root");
} else {
define(PATH, $_GET['p']);
}
} else { define(PATH, "root"); }
//
define(QUERY, "?p=");

// Brotkrümel
$crumb = explode("/", PATH);
if (PATH != 'root' && realpath(PATH)) {
print "…div class='breadcrumbs'>";
$newpath = '';
foreach($crumb as $index => $value) {
$newpath .= $value;

if($index < count($crumb)-1)
print "…a href='" . QUERY . $newpath ."'>$value > ";
// last item //
else
print $value;
$newpath .= '/';
}
print "…div>";
}

if (($retval = scandir(PATH)) !== false) {
$retval = array_filter($retval, 'filter_files');
shuffle($retval);
}

function filter_files($file) {
return ($file != '.' && $file != '..' && $file != '.DS_Store' && $file != 'Thumbs.db');
}

//Schleife - durch alle ausgelesenen Dateien iterieren
foreach ($retval as $value) {
$ext = pathinfo($value, PATHINFO_EXTENSION); //Dokumentformat
if ($ext == "jpg" || $ext == "jpeg" || $ext == "gif" || $ext == "png") {
print "…img class='thumb' src='" . PATH . "/" . $value . "'/>";
} else if ($ext == "txt" || $ext == "rtf") {
$lines = file_get_contents(PATH . '/' . $value);
$lines = str_replace("\n","¶…br/>",$lines);
print "…div class='thumb text'>" . nl2br($lines) . "…div>";
} else if ($ext == "pdf" || $ext == "doc" || $ext == "docx" || $ext == "xls" || $ext == "xlsx" || $ext == "pps" || $ext == "zip" || $ext == "rar") {
print "…div class='thumb file'>…a href='" . PATH . "/" . $value . "'>…img src='img/file.png'/>…span class='desc'>$value…span>";
} else if ($ext == "") { //Ordner, Verzeichnis
print "…div class='thumb folder'>…a href='" . QUERY . PATH . "/" .$value."'>…img src='img/folder.png'/>…span class='desc'>$value…span>";
} else {
//diese Dateiformate werden nicht unterstützt
//können aber jederzeit erweitert werden
}
}

…?>