quakenet:#php Tutorial

Author: Progman, zuletzt bearbeitet von progman @ 2005/03/06 11:31:27

Bitte beachten Sie, dass die Tutorialkapitel zusammenhängen. Wenn sie direkt auf ein Kapitel verlinkt wurden müssen Sie gegebenenfalls die vorherigen Kapitel auch lesen. Achten Sie beim lesen darauf, dass Sie kein Kapitel überspringen.

Benutzung von diversen PEAR-Klassen

  1. Eine URL überprüfen/pingen
  2. Ein Loginsystem
  3. Dauer eines Scriptes messen
  4. Downloadlinks schützen
  5. Mails mit PEAR verschicken

1. Eine URL überprüfen/pingen

Manchmal möchte man prüfen, ob eine URL erreichbar ist oder nicht. Sehr viele benutzen dafür die Funktion fopen. Aber ich will ja die URL nicht öffnen, sondern nur wissen, ob sie existiert. Zuerst pingen wir den Host, zu gucken ob er überhaupt online ist. Denn wenn er garnicht erst online ist braucht wir garnicht erst zu überprüfen, ob die Seite existiert gewscheige denn was die Seite für einen Inhalt hat. Erst wenn der Server auch online ist prüfen wir, ob die Seite auch existiert. Dazu versuchen sie nicht mit fopen zu öffnen, sondern senden die dafür vorgesehende HTTP-Anfrage HEAD. Für all das gibt es, natürlich, diverse PEAR-Klassen, mit denen wir arbeiten werden.

Nun pingen wir zuerst den Server. Dazu brauchen wir aber den Host aus der URL. Wir überprüfen erstmal, ob die URL von der Syntax her korrekt aussieht.

<?php
    error_reporting
(E_ALL);
    require_once
'Validate.php';

    
$url = 'http://localhost/~progman/phpinfo.php';
    
// die zu überprüfende URL

    
if(!Validate::url($url)) {
        die(
'Keine gültige URL');
    }
?>

Wenn die URL von der Syntax her in Ordnung aussieht holen wir uns nun mit parse_url den Host bzw. die IP von der URL.

<?php
    $parse
= parse_url($url);
    
$host = $parse['host'];
?>

Da wir den Host nun haben senden wir ein Ping zum Server. Wir könnten sowas wie exec oder system benutzen um ein Ping zu senden, doch wir haben ja die PEAR::Net_Ping-Klasse dafür. Nachdem wir auch Net/Ping.php mit require_once eingebunden haben holen wir uns erstmal ein neues Ping-Objekt aus der Fabrik.

<?php
    $ping
= Net_Ping::factory();
?>

Die Fehlerabfrage ob alles in Ordnung sei kommt sofort hinterher.

<?php
    $ping
= Net_Ping::factory();
    if(
PEAR::isError($ping)) {
        die(
$ping->getMessage());
    }
?>

Bevor wir ein Ping senden müssen wir es erst einstellen. Dazu gehören z.B. Einstellungen wie Anzahl und Timeout-Zeit. Viele dieser Werte sind vorgegeben, aber die Anzahl der Pings sollten wir auf 1 stellen.

<?php
    $ping
->setArgs(array('count' => 1));
?>

Das Array, was man der Methode setArgs übergibt, kann man entpsrechend der Dokumentation von PEAR::Net_Ping erweitern. Für unsere Zwecke reicht es so. Und nun senden wir ein Ping. Dies geht mit der ping-Methode. Als Parameter dieser Methode kommt der Host oder die IP den/die man pingen möchte.

<?php
    $result
= $ping->ping($host);
?>

Der Ping könnte natürlich auch schiefgegangen sein, warum auch immer. Deswegen auch hier wieder eine Fehlerabfrage.

<?php
    
if(PEAR::isError($result)) {
        die(
$result->getMessage());
    }
?>

Wenn alles in Ordnung ist liefert die ping-Methode ein Net_Ping_Result Objekt zurück, welches ein paar Methoden bereitstellt. Wir benutzen aber eine sehr ungewöhnliche Methode getValue. Diese Methode macht nichts anderes als die Eigenschaft, die man als Parameter übergibt, zurückzuliefern. Es ist das selbe als würde man direkt $obj->_eigenschaft auslesen. Dies ist nicht so toll. Aber was anderes haben wir im moment nicht. Mit dieser Methode lesen wir die Eigenschaft _received aus. Sie gibt an, wie man es vielleicht erraten kann, wieviele Packete (in diesem Fall nur 1) auch wirklich wieder vom Host zurückkam. Eine einfache If-Abfrage reicht zur Überprüfung.

<?php
    
if(!$result->getValue('_received')) {
        die(
'Host nicht erreichbar');
    }
?>

Ich hoffe, es wird irgentwann mal eine Methode getReceived oder so geben...

URL ist gültig, Host ist erreichbar, nun prüfen wir auch nach ob es dir URL auch wirklich gibt. Dazu senden wir die HTTP HEAD-Anfrage. Dazu benutzen wir natürlich auch eine PEAR-Klasse, und zwar die PEAR::HTTP-Klasse, die wir mit require_once 'HTTP.php'; einbinden.

Die HEAD-Abfrage senden wir mit der gleichnamigen Methode head. Als Rückgabewert erhalten wir ein Array. Wir erinnern uns, dass die Antwort vom Server eine gewisse anzahl von Zeilen enthält. Jede dieser Zeile besteht aus der Bezeichnung und aus dem dazugehörigen Wert. Die Ausnahme bildet die erste Zeile, in der der HTTP-Status-Code steht. Jedes Arrayelement enthält dann als Schlüssel die Bezeichnung der Zeile und das Arraywert enthält dann den Wert der Bezeichnung. Zusätzlich, und für unsere Zwecke genau richtig, enthält das Array auch ein Arrayelement mit dem Index response_code welcher dann den entsprechenden Statuscode wie 401 oder 302 enthält. Um zu gucken ob die URL existiert prüfen wir diesen Wert auf 404 (für File not Found) oder 200 (für OK).

Die Methode kann auch ein PEAR-Error Objekt zurückliefern, deswegen sollte die Fehlerabfrage nicht fehlen.

<?php
    $ret
= HTTP::head($url);
    if(
PEAR::isError($ret)) {
        die(
$ret->getMessage());
    }
    switch(
$ret['response_code']) {
        case
404:
            echo
'Die Angegeben URL ist nicht vorhanden (404)';
            break;

        case
200:
            echo
'Die URL existiert';
            break;

        default:
            echo
'unbekannter Statuscode:'.$ret['response_code'];
            break;
    }
?>

So, das wars (schon). Das komplette Script liegt im Scriptarchiv.

2. Ein Loginsystem

Nun schreiben wir ein Loginsystem. Dazu benutzen wir die PEAR::Auth Klasse. Diese müssen wir erstmal mit require_once laden.

<?php
    error_reporting
(E_ALL);
    require_once
'Auth/Auth.php';
?>

Dann erstellen wir ein Auth-Objekt. Dem Konstruktor kann man mehrere Parameter übergeben. Der erste Parameter gibt den Typ des Speichers an, wo die Benutzer gespeichert werden sollen. Da ich gerne mit MySQL arbeite benutze ich hier die Zeichenkette DB. Auf der Manpage von PEAR::Auth kann man nachlesen welche anderen Werte man noch benutzen kann. Der 2. Parameter gibt dann die Einstellung oder Option zu dem gewählten Speichermedium an. Bei DB ist es die DSN und bei File z.B. den Dateinamen. Die anderen Speichermedien inklusive File habe ich noch nicht benutzt, kann also über die Optionen dieser Speichermedien nix sagen. Da muss man entweder auf der Homepage gucken oder sich aus dem Quellcode schlaumachen. Der 3. Parameter gibt den Namen der Loginfunktion an. Nur was ist das? Wenn man ein Auth-Objekt erstellt und 'startet' (dazu kommen wir später) ist dieses Objekt so eingestellt dass es bei einem nichteingeloggten Benutzer ein Loginformular zeigt. Wenn er bereits eingeloggt ist (die Klasse benutzt Sessions) wird das Formular natürlich nicht angezeigt. Mit diesem Parameter kann man nun den Namen der Funktion angeben, die das Formular anzeigt. Die Klasse benutzt dann nicht das interne Loginformular was in der Klasse steht sondern ruft einfach diese Funktion auf. Diese Funktion hat dann einfach nur ein wenig HTML-Code und mehr nicht. Da das interne Loginformular scheiß HTML-Code benutzt (ich sag nur "Tabellen fürs Layout") sollte man hier eine eigene Funktion benutzen. Ein leerer String steht für das interne Loginformular. Der 4. Parameter ist eine einfache false/true Einstellung. Dieser gibt an ob beim 'starten' bzw. überprüfen der Logindaten überhaupt ein Formular angezeigt werden soll oder nicht. Und dies ist natürlich sehr praktisch. Denn, vielleicht hab ich ein fettes Design und diese Klasse schreibt mir überhalb meiner Seite ein scheiß Formularhin, weil das Objekt natürlich oben gestarten werden muss (wegen Session). Damit das nicht passiert setzen wir diesen Parameter auf false. Somit ist auch die Einstellung der Loginfunktion egal, aber wir müssen uns ein Loginformular selber schreiben. Der Aufruf des Konstruktors sieht so aus:

<?php
    $auth
= new Auth('DB',
                     
'mysql://pageuser:foobar@localhost/mypage',
                     
'',
                     
false);
?>

Nun müssen wir die Methode start aufrufen, in der dann eine Session gestartet wird und diverse andere Sachen gemacht werden.

<?php
    $auth
->start();
?>

Den Zustand des Logins überprüfen wir mit der getAuth-Methode. Sie liefert einfach nur true oder false zurück, je nachdem ob die Logindaten stimmen oder der Benutzer bereits eingeloggt ist. Hier können wir z.B. auch unser Loginformular anzeigen lassen, wenn der User nicht eingeloggt ist.

<?php
    
if($auth->getAuth()) {
        echo
'Willkommen im Adminbereich';
    } else {
        echo
'<form action="'.$_SERVER['PHP_SELF'].'" method="post">'."\n";
        echo
'    <fieldset>'."\n";
        echo
'        <legend>Login</legend>'."\n";
        echo
'        <label for="name">Benutzername</label>'."\n";
        echo
'        <input type="text" name="username" />'."\n";
        echo
'        <label for="pass">Password</label>'."\n";
        echo
'        <input type="password" name="password" />'."\n";
        echo
'        <input type="submit" />'."\n";
        echo
'    </fieldset>'."\n";
        echo
'</form>'."\n";
    }
?>

Nun sind wir fertig. In der Session ist nun unser Auth-Objekt. Wir dürfen nur nicht vergessen die Session-ID zu übermitteln. Der Benutzer bleibt nun solange eingeloggt bis der Benutzer den Browser schließt, die Session-ID nicht mehr übertragen wurde oder die Methode logout() ausgeführt wird. Ein Logout-Button, wenn er denn nötig wäre, ist natürlich auch drin.

<?php
    
// ...

    
$auth->start();

    if(isset(
$_GET['action']) AND "logout" == $_GET['action']) {
        
$auth->logout();
    }
    if(
$auth->getAuth()) {
        echo
'Willkommen im Adminbereich';
        echo
'<a href="'.$_SERVER['PHP_SELF'].'?action=logout">'.
             
'Ausloggen</a>'."\n";
    } else {
        
// ...
>

3. Dauer eines Scriptes messen

Manchmal sieht man ganz unten auf den Seiten sowas wie "Generated in x.xx seconds". Die Erweiterung davon ist auch noch die Angabe wieviel Sekunden auf MySQL abfiehl und wieviel auf PHP. Das Grundprinzip ist das Stoppen der Zeit ganz oben und das Stoppen der Zeit ganz unten. Von den beiden Zeiten wird eine Differenz gebildet und man ist dann fertig. Hierzu wir die Funktion microtime verwendet. Die PEAR-Klasse, mit der wir dafür arbeiten, benutzt auch diese Funktion. Aber mit der PEAR-Klasse PEAR::Benchmark geht das alles viel einfacher.

Als erstes müssen wir die entsprechende PEAR-Klasse laden, wie üblich mit require_once.

<?php
    error_reporting
(E_ALL);

    require_once
'Benchmark/Timer.php';
?>

Nun ein Objekt erstellen.

<?php
    $bench
= new Benchmark_Timer();
?>

Und den Timer nun starten.

<?php
    $bench
->start();
?>

Immer dann, wenn wir einen Messpunkt haben möchten, rufen wir die Methode setMarker auf. Als Parameter erwartet diese Methode einen Namen für diesen Marker. Später greifen wir auf diesen Marker zurück. Hinweis: Die Marker beim starten und stoppen des Benchmarks haben die Namen Start und Stop. Nun simulieren wir eine langen Programmcode mit usleep.

<?php
    usleep
(530000);
?>

Nach dieser Zeit stoppen wir wieder den Timer. Dies geht mit der stop-Methode.

<?php
    $bench
->stop();
?>

Mit der TimeElapsed-Methode rufen wir nun ab wie lange php nun zwischen den beiden Punkten gearbeitet hat.

<?php
    
echo $bench->TimeElapsed().' Sekunden hat PHP benötigt';
?>

Somit sind wir fertig. Mit dieser Klasse kann man auch sogenannte Marker setzen. Dann kann man nicht nur die Zeit von oben bis unten messen sondern auch z.B. von Oben bis Marker xyz oder von Marker xyz bis Marker abc. Somit könnte man auch programmieren wieviel Zeit auf MySQL viel und wieviel auf PHP indem man die entsprechenden Marker setzt und die Differenzen ausliest.

4. Downloadlinks schützen

Manchmal möchte man seine Downloads auf seiner Homepage 'schützen', damit diese nicht direkt verlinkt werden können. Nur wie geht man sowas an. Denn wenn man die URL zu der Datei hat, kann man diese auch runterladen. Also müssen wir uns etwas einfallen lassen.

Wir brauchen ein Script, welches für uns den Download startet. In diesem Script überprüfen wir auch, ob versucht wurde die Datei direkt zu laden. Dabei hab ich mir folgendes überlegt. Das Script arbeitet mit Sessions. Beim ersten Aufruf der Datei wird überprüft ob es die Datei gibt und der Dateiname wird in der Session gespeichert. Analog kann man auch mit IDs arbeiten und dann das Script mit download.php?id=45 öffnen. Nachdem die Infos in der Sessions gespeichert wurden läd man das Script neu (Session-ID nicht vergessen). Nun wird einfach der Download gestartet. Dazu benutze ich die PEAR-Klasse PEAR::HTTP_Download. Das fertige Script sieht so aus:

<?php
    error_reporting
(E_ALL);

    require_once
'DB.php';
    require_once
'HTTP/Download.php';

    include
'config.php';

    
$db = DB::connect($dsn);
    if(
DB::isError($db)) {
        die(
$db->getMessage());
    }
    
$db->setFetchMode(DB_FETCHMODE_ASSOC);

    
session_start();

    if(isset(
$_SESSION['datei'])) {
        
HTTP_Download::staticSend(array('file' => $_SESSION['datei']));
        unset(
$_SESSION['datei']);
    } else {
        if(isset(
$_GET['id'])) {
            
// die download-id wurde übergeben
            
$result = $db->query(sprintf('SELECT
                                                 Datei
                                             FROM
                                                 Downloads
                                             WHERE
                                                 ID="%u"'
, $_GET['id']));
            if(
DB::isError($result)) {
                die(
$result->getMessage());
            }
            if(
$result->getRows()) {
                
$row = $result->fetchRow();
                
$_SESSION['datei'] = $row['Datei'];
                echo
'<p>Voll die Fette Werbung damit der Traffic auch bezaht wird</p>';
                echo
'<p>Starte des Downloads von '.$_SESSION['datei'].'</p>';
                echo
'<p>';
                echo
'<a href="'.$_SERVER['PHP_SELF'].'?'.SID.'">';
                echo
'Download starten';
                echo
'</a>';
                echo
'</p>';
                
// eingerückt wegen dem Tutorial,
            
} else {
                echo
'Kein Download mit dieser ID gefunden: '.$_GET['id'];
            }
        } else {
            echo
'Bitte geben sie eine Download-ID an';
        }
    }
?>

5. Mails mit PEAR verschicken

PEAR hat natürlich auch ein paar Klassen um mit Mails zu hantieren. Mal eben nach http://pear.php.net/Mail surfen und die Doku durchlesen ... und schon ist alles klar. Nach dem Installieren der Packete laden wir die Bibliothek.

<?php
    
require_once 'Mail.php';
?>

Nun ein Hauptobjekt erstellen, wie man es auch mit PEAR::DB machen würde.

<?php
    $mail
= Mail::factory('mail');
?>

Auf Fehler prüfen

<?php
    
if (PEAR::isError($mail)) {
        die(
$mail->getMessage());
    }
?>

Um die mail()-Methode benutzen zu können brauche ich ein paar Headerangaben, die mit dem Inhalt/Text der Mail nix zu tun haben. Dazu zählt z.B. "Woher kommt die Mail?", "Welchen Zeichensatz benutzt die Mail", "Was hast sie für ein Titel" und und und.

<?php
    $header
= array();
    
$header['From'] = 'php@bla.com';
    
$header['Subject'] = 'Dies ist eine Testmail';
?>

Nun können wir eine Email verschicken.

<?php
    $result
= $mail->send('dahin@dort.de', $header, 'Dies ist eine Testmail. Toll ne?');
?>

Dann noch auf Fehler prüfen...

<?php
    
if (PEAR::isError($result)) {
        die(
$result->getMessage());
    }
?>

...fertig.

<?php
    
echo 'Mail ist weg, aber ob sie auch ankommt?...';
?>

Das sind ja noch einfache Mails, doch wie sieht es mit HTML-Email (*kotz*), Anhängen und solche Sachen aus. Dazu benutzt man PEAR::Mail_Mime. Damit kann man sich erst seine Email mit den Methoden wie addAttachment() und so zusammenstecken. Dann benutzt man die Methoden get() und headers() um den Inhalt und die Headers der zukünftigen Mail zu bekommen. Diese Werte, die von diesen Methoden kommen, benutzen wir dann als Header- und Message-Parameter für die send()-Methode. Dazu sollte man sich einfach nur die Beispiele in der Dokumentation angucken.

Fragen zum aktuellen Thema

  1. Wie prüfe ich ob eine Variable eine Pear-Error-Klasse ist?
Wie prüfe ich ob eine Variable eine Pear-Error-Klasse ist?

Die PEAR Klasse bietet eine Methode isError an. Mit dieser Methode kann man prüfen ob ein Objekt ein PEAR-Error-Objekt ist. Danach kann man mit der getMessage die Fehlermeldung von dieser Klasse ausgeben.

<?php
    error_reporting
(E_ALL);
    require_once
'PEAR.php';

    
// ein PEAR-Error-Objekt erstellen
    
$err = PEAR::raiseError('Ich bin einfach zu doof', 1337);

    if(
PEAR::isError($err)) {
        echo
$err->getMessage().' ('.$err->getCode().')';
    } else {
        echo
'Alles in Butter';
    }
?>

Dies ist z.B. auch wichtig wenn man eine eigene Klasse schreiben möchte die PEAR Konform sein soll.

Nach oben