# Dateisystem
Wenn die Auslagerungsdatei auf Laufwerk E: eine Anfangsgröße von weniger als 0 MB hat, wird das System möglicherweise keine Debuginformationen speichern können, wenn ein »STOP«-Fehler auftritt.
– Meldung von Windows XP Home
Der Zugriff auf Verzeichnisse und Dateien unter PHP funktioniert ähnlich einfach wie in Perl. Allerdings steht Ihnen hier nicht die Möglichkeit der kurzen Steuerzeichen zur Verfügung. In PHP müssen Sie sich Ihren Weg über verschiedene Funktionen suchen. Dass dies jedoch sehr einfach ist, wird Ihnen dieses Kapitel schildern.
# Verzeichnisliste
Da PHP sehr stark auf die Objektorientierung ausgerichtet ist, wurde für den Zugriff auf Verzeichnisse eine eigene (Pseudo-)Klasse definiert, die über alle Eigenschaften und Methoden verfügt, die erforderlich sind. Diese Klasse heißt dir
.
Eigenschaften der Klasse dir
:
handle
Enthält die Zugriffsnummer für das aktuelle Verzeichnis.path
Enthält den Pfad des aktuellen Verzeichnisses.
Methoden der Klasse dir
:
dir
Der Konstruktor der Klasse; ihm wird das zu öffnende Verzeichnis übergeben.read
Liest nacheinander alle Verzeichniseinträge aus.rewind
Setzt den Verzeichniszeiger auf den ersten Verzeichniseintrag zurück.close
Gibt das Verzeichnis-Handle frei und schließt das Verzeichnis.
Das folgende Listing soll lediglich das aktuelle Verzeichnis öffnen und alle Verzeichniseinträge im Browser ausgeben.
<?php
$d = dir(".");
echo "<h1>$d->path</h1>";
while($eintrag = $d->read())
{
echo "$eintrag<br>\n";
}
$d->close();
?>
Listing 7.1: Einlesen des aktuellen Verzeichnisses und Ausgabe der Einträge im Browser
Das aktuelle Verzeichnis erhalten Sie über den symbolischen Eintrag ».
«. Bei Instanziierung des Objekts $d
wird dem Konstruktor der Klasse dieser symbolische Eintrag übergeben.
$d = dir(".");
Da die Methode read
mit einem Zeiger vergleichbar ist, können Sie sie als Bedingung in einer while
-Schleife aufrufen. Der Zeiger steht anfangs auf dem ersten Verzeichniseintrag und springt einen Eintrag weiter, sobald die Methode read
aufgerufen wird. Als Rückgabewert liefert sie den aktuellen Eintrag. Ist das Ende erreicht, gibt die Methode FALSE
zurück, und die Schleife wird beendet. Der aktuelle Eintrag wird der Variablen $eintrag
zugewiesen und im Anweisungsblock der while
-Schleife ausgegeben. Nachdem die Schleife beendet wurde, wird auch das Verzeichnishandle wieder freigegeben und das Verzeichnis geschlossen.
$d->close();
# Verzeichnis oder Datei
Mit der Funktion is_dir
können Sie überprüfen, ob der als Parameter übergebene Name einem Verzeichnis oder einer Datei entspricht. Ist es ein Verzeichnis, wird TRUE
zurückgegeben, andernfalls FALSE
.
bool is_dir(string pathname)
Das Script aus Listing 7.1 wurde nun um diese Funktion erweitert und gibt den Eintrag fett gedruckt aus, wenn es sich um ein Verzeichnis handelt.
<?php
$d = dir(".");
echo "<h1>$d->path</h1>";
while($eintrag = $d->read())
{
if(is_dir($eintrag))
{
echo "<b>$eintrag</b><br>\n";
}
else
{
echo "$eintrag<br>\n";
}
}
$d->close();
?>
Listing 7.2: Unterschiedliche Formatierung der Ausgabe
Im Anweisungsblock der while
-Schleife wird nun mit Hilfe der Funktion is_dir
überprüft, ob der Eintrag ein Verzeichnis ist, und falls ja, wird er dann fett gedruckt ausgegeben.
Das Einlesen eines Verzeichnisses ist jedoch nicht nur auf das Basisverzeichnis des Webservers beschränkt. Im Prinzip können Sie jedes beliebige Verzeichnis auf dem Server öffnen und einlesen. Sie müssen lediglich das entsprechende Verzeichnis entweder relativ zum aktuellen Verzeichnis oder absolut angeben. Denken Sie aber daran, dass sich hier eine Sicherheitslücke auftut, wenn der Benutzer auf alle Dateien und Verzeichnisse des Servers zugreifen darf.
# Alternative
Alternativ zur Verwendung der Klasse dir
können Sie auch nur mit Funktionen arbeiten, die ein Verzeichnis öffnen, einlesen und wieder schließen.
Die Funktion opendir
öffnet das als Parameter übergebene Verzeichnis und gibt ein Handle für dieses Verzeichnis zurück.
int opendir(string path)
Mit der Funktion readdir
können Sie ein Verzeichnis-Eintrag für Eintrag auslesen. Als Parameter muss das mit opendir
erzeugte Verzeichnishandle übergeben werden. Der aktuelle Eintrag wird dann von der Funktion zurückgegeben.
string readdir(int handle)
Das Äquivalent zur Methode close
der Klasse dir
ist closedir
. Sie erwartet als Parameter das Verzeichnishandle, gibt das Handle frei und schließt das Verzeichnis wieder.
void closedir(int handle)
Das umgeschriebene Script aus Listing 7.1 sieht dann folgendermaßen aus:
<?php
$dirhandle = opendir(".");
while($eintrag = readdir($dirhandle))
{
echo "$eintrag<br>\n";
}
closedir($dirhandle);
?>
Listing 7.3: Einlesen und Ausgeben des aktuellen Verzeichnisses mit Funktionen
# Weitere Verzeichnisfunktionen
Darüber hinaus gibt es noch einige weitere Funktionen, die das Arbeiten mit Verzeichnissen ermöglichen.
Die Funktion rewinddir
z. B. setzt den Verzeichniszeiger wieder auf den Anfang zurück und entspricht der Methode rewind
der Klasse dir
. Als Parameter erwartet die Funktion das Verzeichnishandle.
void rewinddir(int handle)
Das aktuelle Verzeichnis können Sie übrigens auch mit der Funktion getcwd
auslesen, anstatt den symbolischen Link zu verwenden. Sie erwartet keinen Parameter und gibt das aktuelle Arbeitsverzeichnis als String zurück.
string getcwd(void)
Die Funktion chdir
wechselt vom aktuellen in dasjenige Verzeichnis, das als Parameter übergeben wurde. Wurde die Operation erfolgreich durchgeführt, gibt die Funktion TRUE
zurück, andernfalls FALSE
.
bool chdir(string directory)
# Rekursion
Wie auch in Abschnitt 36.2, Rekursion beschrieben, ist eine Rekursion eine Unterteilung einer Hauptaufgabe in mehrere kleinere Teilaufgaben. Die Funktion, die die Hauptaufgabe lösen soll, nimmt diese Unterteilung vor und ruft sich selbst auf, um die Teilaufgabe lösen zu können. Nachfolgend finden Sie die Umsetzung des Perl-Scripts aus Abschnitt 36.2 in PHP. Damit der Wiedererkennungswert jedoch höher ist, wurde beim Auslesen der Verzeichnisse die alternative Variante mit den Funktionen verwendet.
<?php
// Definition der Funktion Unterverzeichnis
function Unterverzeichnis($subverz)
{
echo "<tr><td>$subverz</td><td>";
if($dir = opendir($subverz))
{
$verz = Array();
while($eintrag = readdir($dir))
{
if(($eintrag <> '.') AND ($eintrag <> '..'))
{
if(is_dir("$subverz/$eintrag"))
{
$verz[] = $eintrag;
}
else
{
$groesse = filesize("$subverz/$eintrag");
echo "<a href=\"$subverz/$eintrag\">$eintrag</a> ($groesse Bytes)<br>";
}
}
}
echo "</td></tr>";
foreach($verz as $akt_verz)
{
Unterverzeichnis("$subverz/$akt_verz");
}
closedir($dir);
}
}
// Ermitteln des Wurzelverzeichnisses
$wverz = $_SERVER['DOCUMENT_ROOT'];
// Ausgabe des Anfangs des HTML-Dokumentes
echo "<html><head><title>Wurzelverzeichnis:$wverz</title></head><body>";
echo "<h1>Wurzelverzeichnis: $wverz</h1><table border=\"1\">";
// Erster Aufruf der Funktion "Unterverzeichnis"
Unterverzeichnis($wverz);
// Abschluss des HTML-Dokumentes
echo "</table></body></html>";
?>
Listing 7.4: Rekursives Einlesen von Verzeichnissen mit PHP
Eine Erklärung des Listings finden Sie in Abschnitt 36.2.
# Datei schreiben
Der Zugriff auf Dateien, egal ob lesend oder schreibend, wird immer durch das Öffnen der Datei eingeleitet. Die Funktion fopen
öffnet Dateien und erwartet als ersten Parameter den Dateinamen und als zweiten den Modus, wie auf die Datei zugegriffen werden soll. Konnte der Vorgang erfolgreich durchgeführt werden, liefert die Funktion ein Handle für den Zugriff auf die Datei zurück.
int fopen(string filename, string mode)
Ein Beispielaufruf der Funktion:
$file = fopen("datei.dat","w");
Die Funktion fopen
versucht nun, die Datei datei.dat zum Schreiben zu öffnen. Dass in die Datei geschrieben werden soll, wird der Funktion durch das w
(engl. write = dt. schreiben) als zweiten Parameter mitgeteilt. In der Variablen $file
wird bei Erfolg das Handle zum Zugriff auf die Datei gespeichert.
Der Schreibvorgang selbst erfolgt durch Verwendung der Funktion fputs
. Diese Funktion erwartet als ersten Parameter das Datei-Handle und als zweiten die Zeichenkette, die in die Datei geschrieben werden soll.
int fputs(int handle, string str)
War der Vorgang erfolgreich, liefert die Funktion die Anzahl der geschriebenen Zeichen zurück, andernfalls –1
. Dieses Verhalten kann zur Überprüfung verwendet werden, ob alle Zeichen in die Datei geschrieben wurden, diese Überprüfung wird jedoch in der Regel nicht vorgenommen. Ein Beispiel:
fputs($file,"Hallo Welt!");
Nachdem alle Schreiboperationen durchgeführt worden sind, muss die Datei wieder geschlossen werden. Dies erledigt die Funktion fclose
. Auch sie erwartet als Parameter das Dateihandle. Konnte die Datei geschlossen werden, lautet das Ergebnis der Funktion TRUE
, ansonsten FALSE
.
bool fclose(int handle)
Ein Beispiel:
fclose($file);
Nun zu einem Beispiel, das die Funktionen im Zusammenhang darstellt. In eine Datei sollen lediglich zwei Zeilen geschrieben werden. Diese Datei wird anschließend mit der include
-Funktion im Browser ausgegeben.
<?php
// Schreiben der Datei datei.dat
$file = fopen("datei.dat","w");
if($file)
{
fputs($file,"Hallo Welt!\n");
fputs($file,"Hallo WorldWideWeb!\n");
fclose($file);
}
// Ausgabe der Datei datei.dat im Browser
echo "<h3>Inhalt der Datei datei.dat</h3>";
echo "<pre>";
include("datei.dat");
echo "</pre>";
?>
Listing 7.5: Schreiben der Datei datei.dat und Ausgabe im Browser
Zu Beginn des Scripts wird versucht, datei.dat zum Schreiben zu öffnen. Wurde die Datei geöffnet, werden zwei Zeilen in die Datei geschrieben und das Dateihandle wieder freigegeben. Auch beim Schreiben in Dateien können Sie die Escape-Sequenzen verwenden. In diesem Fall erzeugt \n
einen Zeilenumbruch. Zum Schluss wird die Datei innerhalb eines pre
-Elements ausgegeben.
# Datei lesen
Neben dem Modus w
zum Schreiben von Dateien gibt es auch noch folgende weitere Modi.
Modus | Erklärung |
---|---|
r | Die Datei wird nur zum Lesen geöffnet. Der Dateizeiger wird auf den Anfang der Datei gesetzt. |
r+ | Die Datei wird zum Lesen und Schreiben geöffnet. Der Dateizeiger wird auf den Anfang der Datei gesetzt. |
w | Die Datei wird nur zum Schreiben geöffnet. Existiert die Datei bereits, wird sie überschrieben, andernfalls neu erstellt. |
w+ | Öffnet die Datei zum Lesen und Schreiben. Existiert die Datei bereits, wird sie überschrieben, andernfalls neu erstellt. |
a | Öffnet die Datei nur zum Schreiben. Der Dateizeiger wird auf das Ende der Datei gesetzt. Sollte die Datei noch nicht existieren, wird sie neu erstellt. |
a+ | Öffnet die Datei zum Schreiben und Lesen und setzt den Dateizeiger auf das Ende der Datei. Existiert die Datei nicht, wird sie neu angelegt. |
Tabelle 7.1: Zugriffsmodi für Dateien
Im Zusammenhang mit dem Lesen von Dateien gibt es einige hilfreiche Funktionen. Die wichtigste von allen ist wohl die Funktion fgets
, da sie das Lesen aus einer Datei ermöglicht. Als ersten Parameter erwartet sie das Dateihandle und als zweiten die Angabe, wie viele Zeichen aus der Datei ausgelesen werden sollen. Als Rückgabewert liefert die Funktion die ausgelesenen Zeichen.
string fgets(int handle[, int length])
Wenn Sie die Länge nicht angeben, wird die Datei bis zum Ende ausgelesen. Ein Beispiel:
$dateistr = fgets($file,255);
Dateien sind jedoch nicht immer genau 255 Byte bzw. Zeichen groß. Sie können auch kleiner oder größer sein. Bei jedem Lesevorgang wird der Dateizeiger um die Anzahl der gelesenen Zeichen verschoben. Dabei liest die Funktion jedoch immer nur so viele Zeichen ein, wie noch übrig sind. Um auf das Ende einer Datei reagieren zu können (der Dateizeiger sitzt am Ende der Datei), können Sie die Funktion feof
verwenden. Sie erwartet als Parameter das Dateihandle.
int feof(int handle)
Wurde das Ende der Datei erreicht, liefert die Funktion einen Wert, der ungleich –1
ist.
Äußerst hilfreich ist auch die Funktion file_exists
. Mit ihr können Sie die Existenz einer Datei überprüfen. Existiert die als Parameter übergebene Datei, entspricht der Rückgabewert der Funktion TRUE
, andernfalls FALSE
.
bool file_exists(string filename)
Das folgende Beispiel soll das Lesen und Schreiben einer Datei anhand einer Umfrage darstellen.
<?php
if($_GET)
{
$filename = "abstimmung.txt";
$wahl = $_GET['os'] - 1;
if(file_exists($filename))
{
$file = fopen($filename,"r");
if($file)
{
$stand = explode("#",fgets($file));
fclose($file);
$stand[$wahl]++;
$file = fopen($filename,"w");
if($file)
{
fputs($file,"$stand[0]#$stand[1]#$stand[2]#$stand[3]");
fclose($file);
}
echo "<h4>Stand</h4>";
echo "Windows = $stand[0]<br>\n";
echo "Linux = $stand[1]<br>\n";
echo "MacOS = $stand[2]<br>\n";
echo "Anderes = $stand[3]<br>\n";
}
}
else
{
echo "$filename existiert nicht!";
}
}
else
{
echo "<form action=\"$PHP_SELF\" method=\"GET\">";
echo "<h4>Welches Betriebssystem verwenden Sie?</h4>";
echo "<input type=\"radio\" name=\"os\" value=\"1\">Windows<br>\n";
echo "<input type=\"radio\" name=\"os\" value=\"2\">Linux<br>\n";
echo "<input type=\"radio\" name=\"os\" value=\"3\">MacOS<br>\n";
echo "<input type=\"radio\" name=\"os\" value=\"4\">Anderes<br>\n";
echo "<input type=\"submit\" value=\"Abstimmen\">\n";
echo "</form>";
}
?>
Listing 7.6: PHP-Umfrage-Script, das das Ergebnis in einer Datei speichert
Zu Beginn des Scripts wird überprüft, ob mit einem HTML-Formular und der GET-Methode Daten an das Script übergeben wurden.
Wurden keine Daten übergeben, wird ein HTML-Formular ausgegeben, das als Datenempfänger das eigene PHP-Script erhält. Außerdem wird eine Radiobuttongruppe ausgegeben, die vier Auswahlmöglichkeiten bietet (Windows, Linux, Mac OS, Anderes).
Wenn Daten übermittelt wurden, wird zunächst der Variablen $wahl
die übermittelte Auswahl abzüglich 1 zugewiesen. Sie dient später zu Identifikation des richtigen Elements eines Arrays. Anschließend wird überprüft, ob die Datei abstimmung.txt existiert. Wenn nicht, wird eine Fehlermeldung ausgegeben. Ansonsten wird die Datei zum Lesen geöffnet. Es folgt dann eine Kombination von zwei Funktionen. Der Inhalt der Datei, der mit fgets($file)
eingelesen wurde, wird als zweiter Parameter an die Funktion explode
übergeben. Letztere soll den String, dem sie übergeben wird, anhand des Rautezeichens #
in Teile zerlegen, die im Array $stand
gespeichert werden. Die Datei wird geschlossen, und das Element mit dem Index, der dem Wert von $wahl
entspricht, wird um 1 inkrementiert. Daraufhin wird wieder die Datei abstimmung.txt geöffnet, und alle Elemente des Arrays $stand
werden durch Rautezeichen #
getrennt in die Datei geschrieben. Nachdem die Datei geschlossen worden ist, wird das aktuelle Abstimmungsergebnis im Browser ausgegeben.
Der Aufbau der Datei abstimmung.txt sieht folgendermaßen aus:
0#0#0#0
Die erste 0 steht für Windows, die zweite für Linux, die dritte für Mac OS und die vierte für Anderes. Die Zahlen werden nach jeder Auswahl eines Radiobuttons um 1 erhöht.
Auch wenn die Möglichkeit besteht, gleichzeitig sowohl lesend als auch schreibend auf eine Datei zuzugreifen, sollten Sie dies nur in den seltensten Fällen tun. Es kann ansonsten passieren, dass Sie den Inhalt der Datei unwiederbringlich löschen.
# Dateieigenschaften
Verschiedene Funktionen geben die unterschiedlichsten Informationen zu einer Datei zurück. Deshalb werden Sie in diesem Kapitel eine Klasse namens fileinfo
erstellen, die die Informationen ermittelt und bereitstellt.
Der Quellcode der Klasse:
<?php
class fileinfo
{
var $filename;
var $size;
var $type;
var $lastChange;
var $lastChangeUnix;
var $owner;
var $readable;
var $writeable;
var $executable;
function fileinfo($filename)
{
$this->filename = $filename;
$this->size = filesize($filename);
$this->type = filetype($filename);
$this->lastChange = strftime("%d.%m.%Y %H:%M",filemtime($filename));
$this->lastChangeUnix = filemtime($filename);
$this->owner = fileowner($filename);
$this->readable = is_readable($filename);
$this->writeable = is_writeable($filename);
$this->executable = is_executable($filename);
}
}
?>
<?php
require('class_fileinfo.php');
$fi = new fileinfo('abstimmung.txt');
echo "Dateiname: $fi->filename<br>\n";
echo "Gr��e: $fi->size Byte<br>\n";
echo "Type: $fi->type<br>\n";
echo "Letzte �nderung: $fi->lastChange<br>\n";
echo "Besitzer: $fi->owner<br>\n";
echo "Lesbar: $fi->readable<br>\n";
echo "Schreibbar: $fi->writeable<br>\n";
echo "Ausf�hrbar: $fi->executable";
?>
Listing 7.7: Einsatz der Klasse fileinfo
Die Klasse fileinfo
besteht lediglich aus einer Funktion und einer Reihe von Variablen. Die Funktion ist der Konstruktor der Klasse, initialisiert die Eigenschaften und wendet dabei die einzelnen Funktionen von PHP an. Die einzige Eigenschaft, die ohne Funktion ermittelt wird, ist natürlich der Dateiname, der in der Eigenschaft $filename
gespeichert wird.
Die Dateigröße wird durch die Funktion filesize
ermittelt. Sie erhält als Parameter den Dateinamen und liefert die Größe in Bytes zurück.
Die Funktion filetype
ermittelt den Typ der Datei und gibt ihn als String zurück. Als Parameter erwartet die Funktion den Dateinamen. Mögliche Typen sind fifo
, char
, dir
, block
, link
, file
und unknown
.
Die letzte Änderung der Datei wird einmal als UNIX-Zeitwert und einmal als formatierter String gespeichert. Den Änderungszeitpunkt ermittelt die Funktion filemtime
, die als Parameter den Dateinamen erwartet und den UNIX-Zeitstempel zurückgibt.
Die Funktion fileowner
ermittelt den Besitzer der Datei. Leider ist diese Funktion jedoch UNIX-spezifisch, weshalb sie unter Windows keinen zuverlässigen Wert liefern wird. Als Parameter erwartet auch sie den Dateinamen und gibt den Besitzer zurück.
Die Funktionen is_readable
, is_writeable
und is_executable
überprüfen, ob die als Parameter übergebene Datei zu lesen, zu schreiben und ausführbar ist. Der Rückgabewert der Funktionen ist entweder TRUE
oder FALSE
.
Die Verwendung dieser Klasse ist wesentlich übersichtlicher und einfacher, als jedes Mal die Funktionen einzeln aufzurufen. Natürlich ist bei dieser Klasse immer noch Platz für Erweiterungen oder Änderungen. Welche Sie vornehmen, bleibt Ihren eigenen Wünschen überlassen.
# Dateisystemoperationen
Gelegentlich wird es bei der Arbeit mit Dateien und Verzeichnissen vorkommen, dass Sie Dateien umbenennen, verschieben oder Verzeichnisse erstellen oder löschen möchten. Mit den entsprechenden Funktionen werde ich mich in diesem Kapitel beschäftigen.
Mit der Funktion copy
können Sie eine Datei kopieren. Als ersten Parameter erwartet sie den Quelldateinamen und als zweiten Parameter den Zieldateinamen. Konnte die Datei kopiert werden, liefert die Funktion TRUE
zurück, andernfalls FALSE
.
bool copy(string source, string destination)
Die Funktion rename
ermöglicht entweder das Umbenennen oder das Verschieben einer Datei (wenn Verzeichnisse mit angegeben werden). Als Parameter müssen die Quelle und das Ziel angegeben werden. Der Wert TRUE
wird dann zurückgegeben, wenn die Operation erfolgreich durchgeführt werden konnte.
bool rename(string old, string new)
Für den Fall, dass Sie eine Datei löschen möchten, steht die Funktion unlink
zur Verfügung. Sie erwartet als Parameter lediglich den Dateinamen und gibt TRUE zurück, wenn die Datei gelöscht werden konnte.
bool unlink(string filename)
Mit der Funktion mkdir
können Sie ein neues Verzeichnis erstellen. Als Parameter müssen Sie lediglich den Namen für das neue Verzeichnis übergeben. Sie erhalten TRUE
, wenn das Erstellen des Verzeichnisses erfolgreich war.
bool mkdir(string dirname)
Umgekehrt können Sie ein Verzeichnis mit rmdir
löschen. Der Name des zu löschenden Verzeichnisses wird als Parameter übergeben. Damit das Verzeichnis gelöscht werden kann, muss es leer sein. TRUE
wird zurückgegeben, wenn es gelöscht wurde.
bool rmdir(string dirname)
# Zusammenfassung
- Die Klasse
dir
kann alternativ zu den Funktionenopendir
,readdir
undclosedir
verwendet werden. - Die Funktionen
rewinddir
,getcwd
undchdir
erweitern die Arbeitsmöglichkeiten mit Verzeichnissen. - Vor einem Schreib- oder Lesevorgang muss eine Datei mit
fopen
geöffnet und der Zugriffsmodus angegeben werden. Geschlossen wird sie mit der Funktionfclose
. - Die Funktion
fgets
liest aus einer Datei. - Die Funktion
fputs
schreibt in eine Datei. - Die selbst definierte Klasse
fileinfo
ermittelt alle wichtigen Eigenschaften einer Datei.
# Fragen und Übungen
- Schreiben Sie ein Script, das ein Verzeichnis ausliest und dabei nur Dateien berücksichtigt.
- Erweitern Sie das Script aus Aufgabe 1 um die Ausgabe der Größe, des Typs und des letzten Änderungszeitpunktes der Datei.
- Erweitern Sie das Script um die Möglichkeit, dass der Benutzer die Datei mit einem Link öffnen kann.