1unitedpower: Teil 3: Datenbank-Schicht in PHP (zusammengefügt), Ausgabe in HTML, finale Anwendung, Zusatzaufgabe

Beitrag lesen

Setzen wir alle Funktions-Definitionen nun in das Rohgerüst unser AccessLogRepository-Klasse ein, enthalten wir die folgende, vollständige Klassen-Definition. Den Quelltext speichern wir in der Datei mit dem Namen AccessLogRepository.php.

<?php
declare(strict_types=1);

namespace SelfHtml\Counter;

use \PDO;

final class AccessLogRepository
{
    private $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    /**
     * Erzeugt einen neuen Datenbank-Eintrag für die angebene IP-Adresse
     * und den aktuellen Zeitstempel.
     *
     * @param string $ip IP-Adresse des zu speichernden Log-Eintrags
     *
     * @return void
     */
    public function log(string $ip) : void
    {
        static $query = 'INSERT INTO `access_log` (`ip`) VALUES (:ip);';
        $this->pdo->prepare($query)->execute([':ip' => $ip]);
    }

    /**
     * Liest die Anzahl der verschiedenen IP-Adressen aus, die in den vergangenen
     * X Sekunden einen Zugriff durchgeführt haben.
     *
     * @param int $maxAge Das maximale Alter in Sekunden, das ein Eintrag haben darf,
     * damit die dazugehörige IP-Adresse in die Zählung aufgenommen wird.
     *
     * @return int
     */
    public function countIpsYoungerThan(int $maxAge) : int
    {
        static $query = <<<SQL
            SELECT COUNT(DISTINCT `ip`)
            FROM `access_log`
            WHERE TIMESTAMPDIFF(
                    SECOND,
                    `access_time`,
                    NOW()
                ) <= :maxAge;
            SQL;
        return (int) $this->pdo->prepare($query)
            ->execute(['mageAge' => (string) $magAge])
            ->fetchColumn();
    }

    /**
     * Liest die Anzahl allers Sessions aus der Datenbank aus.
     * Eine Session wird von Log-Einträgen der selben IP-Adresse gebildet,
     * die zeitlich nahe beianander stehen.
     *
     * @param int $sessionLifeTime Die maximale Dauer in Sekunden, die zwischen zwei
     * Log-Einträgen der selben IP-Adresse vergangen sein darf, damit sie zur selben
     * Session gezählt werden.
     *
     * @return int
     */
    public function countAllSessions(int $sessionLifeTime) : int
    {
        static $query = <<<SQL
            SELECT COUNT(*) FROM (
                SELECT
                    TIMESTAMPDIFF(
                        SECOND,
                        `access_time`,
                        LEAD(`access_time`) OVER (
                            PARTITION BY `ip`
                            ORDER BY `access_time`
                        )
                    ) as `delta`
                FROM `access_log`
            ) `t`
            WHERE `delta` IS NULL OR `delta` > :session_life_time;
            SQL;
        return (int) $this->pdo->prepare($query)
            ->execute([':session_life_time' => (string) $sessionLifeTime]))
            ->fetchColumn();
    }
}

Ausgabe in HTML

Wir brauchen als nächstes noch eine Funktion für die Ausgabe in HTML. Die Funktion bekommt die beiden Werte des Statistik-Moduls als Parameter übergeben, setzt diese in die richtigen Stellen des HTML-Codes ein und gibt den resultierenden HTML-String zurück. Den Quelltext legen wir in einer Datei namens View.php ab.

<?php
declare(strict_types=1);

namespace SelfHtml\Counter;

function html(int $visits, int $count) : string
{
    $visitsEscaped = htmlspecialchars((string) $visits);
    $onlineEscpaed = htmlspecialchars((string) $online);
    return <<<html
        <!DOCTYPE html>
        <html lang="de">
            <head>
                <title>SelfHtml Counter Beispiel</title>
            </head>
            <body>
                <label for="vistis">Anazahl Besucher Ingesamt</label>
                <output id="visits">$visitsEscaped</output>
                <label for="online">Anzah Beucher Online</label> 
                <output id="online">$onlineEscpaed</output>
            </body>
        </html>
        html;
}

Die finale Anwendung verkabeln

Wir haben nun alle Module, dir wir brauchen: Ein Modul, das zwischen der Datenbank und der Geschäftslogik vermittelt, und ein Modul, das die Ausgabe in HTML formatiert. Im letzten Teil dieses Turoials, müssen wir die Module nun noch zu einer Gesamtanwendung verkabeln.

Als erstes müssen wir unsere beiden Module in die Anwendungen einbinden. Das machst du mit require_once. Danach müssen wir ein paar Konfigurations-Werte setzen. Dazu gehören unsere Datenbankverindungsdaten; die Dauer in Sekunden, die eine Person als online gezählt werden soll; und die Dauer in Sekunden, die zwischen zwei Anfragen der selben IP-Adresse vergangen sein muss, damit sie in der Gesamtzählung einzeln berücksichtigt werden.

Wenn wir alle Konfigurations-Werte haben, können wir die Datenbank-Verbindung aufbauen und unser AccessLogRepository-Modul initialiseren.

Nach der Initialisierung, loggen wir die aktuelle IP-Adresse, lesen unsere beiden Statistik-Werte aus und übergeben sie an unsere Ausgabe-Funktion.

<?php
declare(strict_types=1);

namespace SelfHtml\Counter;

use \PDO;

// Module einbinden

require_once('AccessLogRepository.php');
require_once('View.php');

// Konfiguration
$dsn = 'mysql:host=localhost;dbname=testdb';
$username = 'username';
$password = 'password';
$sessionLifetime = 86400; // 86400 Sekunden = 1 Tag
$onlineTime = 600; // 600 Sekunden = 10 Minuten

// Persistenz-Schicht initialisieren
$pdo = new PDO($dsn, $username, $password);
$repository = new AccessLogRepository($pdo);

// Aktuellen Zugriff loggen
$repository->log($_SERVER['REMOTE_ADDR']);

// Statistiken auslesen
$visits = $repository->countAllSessions($sessionLifetime);
$online = $repository->countIpsYoungerThan($onlineTime);

// Ausgabe
echo html($visits, $online);

Das war es. Du kannst den Quelltext zum Beispiel in einer Datei mit dem Namen index.php speichern.

Zusatzaufgabe

Als Übung, um das Material dieses Tutorials zu vertiefen, könntest du die Statistik um einen dritten Wert erweitern, der die Gesamtanzahl aller Aufrufe unabhängig von der IP-Adresse und der verstrichenen Zeit zwischen zwei Log-Einträgen ausgibt. Überlege dir dazu schrittweise, wie du das Datenbank-Modul und das Ausgabe-Modul anpassen musst. Erst im letzten Schritt solltest du die index.php-Datei entsprechend anpassen.