Kategorien
E-Business Programmierung SEO & Online-Marketing Sonstige Programmiersprachen

PHP und .htaccess: Lesbare URLs mit variablen Verzeichnisnamen

Bei dynamischen Websites, die zum Beispiel mit PHP realisiert werden, wird oftmals eine komplette Website über eine einzige PHP-Datei gesteuert. Dabei werden Variablen als URL-Parameter übergeben und Seiten mit unterschiedlichen Inhalten generiert. So entstehen in der Regel unschöne URLs, die zum einen sehr lang und zum anderen weder für Menschen noch für Suchmaschinen verständlich sind. Mit ein bisschen PHP-Code lässt sich das ändern.

Mit ein paar Zeilen in der .htaccess-Datei lassen sich kurze und prägnante URLs generieren, bei denen Variablen nicht mehr über URL-Parameter sondern als Verzeichnisnamen übertragen werden. Aus

http://www.example.com/index.php?rubrik=leistungen&seite=beratung

wird dann

http://www.example.com/leistungen/beratung/

und suggeriert, dass es sich tatsächlich um Verzeichnisse handelt, in denen die Inhalte der Seite liegen.

Vorteile

Der Vorteil ist zum einen, dass die URLs deutlich kürzer werden. Gerade wenn man URLs beziehungsweise Links per Mail versendet oder gar in gedruckter Form veröffentlicht, ist eine kurze und einprägsame Adresse von großer Bedeutung. Ein weiterer Grund, auf URL-Parameter zu verzichten, liefern Suchmaschinen wie Google selbst. Sie raten davon ab, zu lange URLs zu verwenden, da solche Seiten deutlich schlechter bewertet und demzufolge auch nachrangig berücksichtigt werden bei der Anzeige von Suchergebnissen.

Laut Googles Starter Guide für die Suchmaschinenoptimierung sollten URLs

  1. kurz sein, auf URL-Parameter sowie Session-IDs weitestgehend verzichten,
  2. konkrete Bezeichnungen haben anstatt allgemeiner („leistungen“ statt „seite2“) und
  3. nicht zu viele Unterverzeichnisse besitzen.

Außerdem kann man mit der oben genannten Methode verschleiern, dass eine Website mit PHP programmiert ist. So erschwert man es Hackern, Sicherheitslücken eines Servers bzw. einer serverseitigen Programmiersprache auszunutzen, da die verwendete Programmiersprache nicht ersichtlich ist.

Drei Zeilen in die .htaccess-Datei

Um die Variablenübergabe über Verzeichnisnamen zu steuern, reichen drei Zeilen in der .htaccess-Datei aus:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* /index.php

Die erste Zeile startet die „Rewrite Engine“ von Apache. In der zweiten Zeile wird die Bedingung für die in der dritten Zeile folgende Regel angegeben. Die Bedingung prüft, ob es sich bei der angeforderten Datei tatsächlich um eine existierende Datei handelt.

Achtung: Dies ist jedoch nur möglich wenn im Apache Server das Modul: modrewrite aktiviert ist. Andernfalls verursacht der soeben eingefügte Code einen Fehler und Ihre Webseite bleibt weiß.

Die Bedingung ist erfüllt, wenn es sich nicht um eine Datei handelt. Die Flag „-f“ prüft, ob es sich um eine Datei handelt, „!-f“ prüft – so wie es in dem Beispiel gebraucht wird –, ob es sich nicht um eine Datei handelt:

RewriteCond %{REQUEST_FILENAME} -f # Bedingung erfüllt, wenn es eine Datei ist
RewriteCond %{REQUEST_FILENAME} !-f # Bedingung erfüllt, wenn es keine Datei ist

Die Regel in der dritten Zeile leitet alle Anfragen („.*“), die nicht auf eine existierende Datei verweisen, an die Datei „/index.php“ weiter. Handelt es sich um eine exisitierende Datei (z. B. eine Grafik oder ein PDF-Dokument), so wird diese ganz normal aufgerufen, dargestellt bzw. zum Download angeboten.

Über die PHP-Datei lässt sich dann die URL auslesen und weiterverarbeiten. Alternativ lässt sich die Übergabe der URL-Parameter an die PHP-Datei auch direkt über den Eintrag in die .htaccess-Datei realisieren. Dazu die letzte Zeile der .htaccess-Datei austauschen:

RewriteRule ^([\w]+)/?([\w]+)? /index.php?rubrik=$1&seite=$2

Über die regulären Ausdrücke „([\w]+)“ werden Zeichenketten bestehend aus Buchstaben und Zahlen, die durch einen Schrägstrich getrennt sind, als Variable an die PHP-Datei übergeben. Das Fragezeichen hinter dem Schrägstrich und dem zweiten Ausdruck signalisiert, dass diese Werte optional sind. Das heißt, es kann auch nur ein Wert übergeben werden, und zwar für den Parameter „rubrik“.

Variablen auslesen mit PHP

Wer die erste Variante von „RewriteRule“ nutzt, muss die Variablen „rubrik“ und „seite“ per PHP aus der URL auslesen. Damit die „index.php“ die Variablen aus der URL auslesen kann, wird die URL an eine PHP-Variable übergeben:

$url = $_SERVER["REQUEST_URI"];

Die vorderfinierte Variable „$_SERVER[„REQUEST_URI“]“ enthält die URL, über die die PHP-Datei aufgerufen wurde. Im aktuellen Beispiel ist das „/leistungen/beratung/“.

Mit der Funktion „explode()“ wird die URL in ihre Bestandteile zerlegt und als Array weiterverarbeitet:

$url = explode("/", $url);

Da die Zeichenkette „/leistungen/beratung/“ anhand des Schrägstrichs zerlegt wird, werden dem Array in diesem Fall vier Werte übergeben, wobei der erste und der letzte Wert jeweils eine leere Zeichenkette sind, da vor dem ersten und nach dem letzten Schrägstrich keine Zeichen stehen.

Die eigentlichen Werte „leistungen“ und „beratung“ befinden sich an Position zwei und drei des Arrays. Sie werden jeweils einer Variablen zugeordnet:

$rubrik = $url[1];
$seite = $url[2];

In der zweiten Variante des „RewriteRule“-Eintrags in der .htaccess-Datei können die URL-Parameter direkt über „$_GET“ ausgelesen werden, ihne die Werte aus der URL zu extrahieren:

$rubrik = $_GET["rubrik"];
$seite = $_GET["seite"];

Wird nur ein Verzeichnis angeben (z. B. „/leistungen/“), so bleibt die Variable „$seite“ leer. Was nicht funktioniert, ist die Übergabe eines Wertes nur für die Variable „$seite“, da der erste Verzeichnisname immer der Variablen „$rubrik“ zugewiesen wird.

Ausnahmen festlegen

Möglicherweise will man nicht alle URL-Anfragen an die index.php weiterleiten. Das Verzeichnis „downloads“ soll z. B. nicht von der Datei „index.php“ sondern von „downloads.php“ verarbeitet werden. In diesem Fall fügt man nach der ersten Zeile der .htaccess-Datei folgende Zeile ein:

RewriteRule ^downloads$ /downloads.php

Jetzt wird die URL „/downloads/“ an die Datei „downloads.php“ verwiesen und kann von dort verarbeitet werden.

Fazit

Die hier vorgestellte Methode, um URL-Paramter zu übertragen, wird insbesondere von Content-Management-Systemen eingesetzt. Bei TYPO3 sorgt zum Beispiel die Erweiterung RealURL für solche kurzen und suchmaschinen-freundlichen URLs.

Vom Prinzip her arbeitet RealURL genau so wie die hier beschriebene Methode.

(mm),

Kategorien
Inspiration Programmierung Sonstige Programmiersprachen Tutorials

PDF-Dokumente mit PHP erzeugen

PDF-Dateien zu erstellen, ist schon lange kein Problem mehr. Spätestens seit OpenOffice die Möglichkeit bot, Textdokumente ins PDF-Format zu konvertieren (und möglicherweise aufgrund dessen andere namhafte Anbieter nachzogen), ist es für jedermann leicht möglich, seine Informationen in Form eines PDF-Dokumentes anzubieten. Anders sieht es aus, wenn man mal schnell auf seiner Website aktuelle Inhalte „zur Laufzeit“ dynamisch in einer PDF-Datei anbieten möchte.

Das wäre zum Beispiel nötig, wenn man Benutzereingaben aus Formularen in diese PDF-Datei integrieren muss. Ein mögliches Szenario wäre ein Bestellformular, welches nach erfolgter Bestellung dem Benutzer die Möglichkeit bietet, eine Bestellung als PDF-Datei auszudrucken/herunterzuladen.

Lösungsansatz / Alternativen

Wie immer gibt es mehrere Lösungen. In unserem Fall wird die PDF-Erzeugung durch Verwendung der Klasse FPDF gezeigt. Diese Klasse ist frei erhältlich und darf sowohl für private als auch für kommerzielle Zwecke genutzt werden. Das ist der erste Grund, weshalb wir FPDF hier verwenden, der zweite ist die Stabilität. Die FPDF-Klasse wird seit 2001 entwickelt und kann sich heute auf die Fahne schreiben, eine relativ einfach zu verwendende, erprobte und stabile PHP-Klasse zu sein. Eine Doku zu FPDF gibt es hier.

Als Alternative zu FPDF muss an erster Stelle PDFlib genannt werden. Diese Klasse ist jedoch nicht auf jedem Webserver verfügbar und Einrichtung und Umfang sind nicht trivial.  Wer sich die Sache trotzdem ansehen möchte, kann diese schön geschriebene Anleitung lesen. Eine andere interessante Alternative könnte TCPDF sein. Der Vollständigkeit halber seien „PDFlib Lite“, „CPDF“ und „Zend_Pdf“ für das Zend-Framework erwähnt.

Grundlagen / eigene Klasse

Für unsere Übung sollte auf Ihrem Webserver PHP5 laufen. Falls Sie keine Möglichkeit haben PHP5 zu nutzen, sind ein paar kleine Änderungen am Quellcode notwendig, dass die Skripte auch auf PHP4 lauffähig sind. Dazu am Ende mehr.

Am Besten legen Sie sich für die folgenden Versuche ein neues Verzeichnis an (so können Sie dieses eventuell schnell und problemlos wieder löschen, wenn Ihnen die Sache am Ende nicht gefällt). In dieses Verzeichnis entpacken Sie die FPDF-Klasse, die Sie hier herunterladen. Erstellen Sie ein weiteres Verzeichnis neben dem soeben entpackten fpdf-Verzeichnis und nennen es twpdf. Dies ist nur ein Vorschlag, Sie haben freie Auswahl. Manchmal jedoch ist es vorteilhaft, bei den selbst erstellten Verzeichnis-, Datei-, Funktions- und Variablennamen eine Art Vorzeichen anzuhängen, um im Code immer schnell erkennen zu können, was eigene Tests und was vorgegebene, erprobte Dinge sind. Alles was wir in unseren Übungen „probieren“ erhält die „Vorsilbe“ tw, so beugen wir dem versehentlichen Löschen der FPDF-Klasse vor.

Am Ende sollten Sie jedenfalls eine Verzeichnisstruktur ähnlich der im folgenden Bild vorliegen haben:

Testklasse01

Beginnen wir mit einer kleinen Testklasse um die grundlegenden Dinge kennenzulernen und gleichzeitig eine Art Vorlage für alle späteren Arbeiten zu haben.

// unsere Testklasse erbt von der FPDF-Klasse
class Testklasse01 extends FPDF {

	// hier werden die Variablen für diese Klasse deklariert (also: "bekanntgemacht")
	private $twVariable01 = "";
	private $twVariable02 = ""; 

	// der Konstruktor dieser Klasse
	function __construct() {
		// Konstruktor der vererbenden Klasse aufrufen (also den von FPDF)
		parent::__construct("P", "mm", "A4"); // L=Querformat(Landscape), P=Hochformat(Portrait)

		// hier werden die Variablen dieser Klasse initialisiert (also: "gefüllt")
		$this->twVariable01 = "Hallo, ich bin der Probetext aus Testklasse01";
		$this->twVariable02 = date("d.m.Y");

		// Einstellungen für das zu erstellende PDF-Dokument
		$this->SetDisplayMode(100);      // wie groß wird Seite angezeigt(in %)

		// Seite erzeugen (sozusagen: starten)
		$this->AddPage();
	}	

	// eine Funktion zur Anzeige des Inhalts
	function Header() {
		$this->SetFont("Arial","B","16");                    // Schrift
		$this->SetTextColor(255, 000, 000);                  // Schriftfarbe
		$this->SetXY(20, 50);                                // Position
		$this->Cell(90, 10, $this->twVariable01, 1, 1, "L"); // Box(Textbox)
	}	

	function Footer() {
		// Farben und Schrift allgemein
		$this->SetFont("Courier","I","9");
		$this->SetTextColor(180);
		$this->SetXY(25, 288);
		$this->Cell(170, 5, "dieses Dokument wurde am ". $this->twVariable02. " erstellt", 1, 1, "R");
	}
}
// ACHTUNG: darf kein Zeichen hinter phpend sein (auch kein Leerzeichen) !!!

Testklasse01.php

Diese kleine Testklasse könnte einfacher aufgebaut sein, doch für umfangreichere PDF-Dokumente später ist es ratsam, sich schon jetzt diesen Aufbau mit Variablendeklarationen, Konstruktor, Header uns Footer anzugewöhnen. Der abgebildete Quellcode sollte anhand der eingefügten Kommentare selbsterklärend sein. Trotzdem an dieser Stelle Erläuterungen zu den FPDF-Funktionen:

  • Zeile 12: Ruft den Konstruktor der (vererbenden) FPDF-Klasse auf und setzt dabei das Papierformat, Maßeinheit und Papiergröße.
  • Zeile 19: SetDisplayMode() legt fest, wie das Dokument angezeigt werden soll, also mit welchem Zoomfaktor.
  • Zeile 22: AddPage() Erzeugt eine neue Seite im Dokument (beziehungsweise überhaupt eine Seite).
  • Zeilen 26 bis 32: Header() Erzeugt den Header der Seite. Kann jedoch auch für den gesamten Seiteninhalt verwendet werden.
    • SetFont() Bestimmt Schriftart, Style (B=fett, I=kursiv, U=unterstrichen) und die Schriftgröße. Die geringe Auswahl an Schriftarten ist derzeit das größte Manko von FPDF, aber mal Hand aufs Herz, wieviele verschiedene Schriftarten verwendet man schon in einem Dokument?
    • SetTextColor() Bestimmt die Textfarbe. Angabe bsp. (220, 000, 110) oder nur (220) (steht für (220, 220, 220)).
    • SetXY() Position von linken oberen Seitenrand (x=horizontal, y=vertikal). Bestimmt den Punkt, an dem es weitergeht.
    • Cell() Zeichnet eine neue Zelle (oder halt Box) im Dokument. Diese kann optional Text, einen Rahmen und einen Hintergrund erhalten. Die linke obere Ecke beginnt an dem zuvor mit SetXY() zugewiesenem Punkt. Die Zellenbreite und -höhe wird in den beiden ersten Parametern festgelegt.
  • Zeilen 35 bis 41: Footer() Die Fußzeile sozusagen.

php-Skript, welches das PDF-Dokument ausgibt, indem es die Testklasse01 benutzt

Es folgt der Code für die php-Datei. Erläuterungen folgen im Anschluss.

// ACHTUNG: darf kein einziges Leerzeichen vor phpstart sein, wegen header!

// FPDF-Klasse und das Verzeichnis der FPDF-Schriftarten einbinden
define("FPDF_FONTPATH","fpdf16/font/");
include_once('fpdf16/fpdf.php');

// unsere selbsterstellte Testklasse01 einbinden
include_once("twpdf/Testklasse01.php");

// pdf erzeugen (aus unserer selbsterstellten Testklasse01)
$pdf = new Testklasse01();  

// pdf ausgeben (im Browser oder in Datei schreiben)
$pdf->Output();   // Ausgabe (wenn in Datei schreiben, dateiname in Klammer)

// ACHTUNG: in der aufgerufenen Klasse darf kein Leerzeichen hinter phpende sein!

testseite01.txt

  • Zeile 4 und 5: Hier werden die FPDF-Klasse und die Schriftarten eingebunden.
  • Zeile 8: Hier binden wir unsere selbst erstellte Klasse ein.
  • Zeile 11: Ein Objekt erzeugen von der Testklasse01.
  • Zeile 14: Output() Output gibt das PDF-Dokument aus, beziehungsweise, wenn man als Argument einen Dateiname angibt, dann wird eine PDF-Datei auf dem Server angelegt.

Wenn Sie kein PHP5 auf Ihrem Webserver nutzen können

Wenn Ihnen kein PHP5 zur Verfügung steht, benutzen Sie für dieses Beispiel folgende Dateien.

Der Unterschied dieser beiden Dateien zu den vorgestellten Dateien ist gering. Hier wurden nur die „private/public-Angaben“ umgeändert in „var“ und der Konstruktoraufruf der vererbenden Klasse hat eine andere Syntax. Diese kleinen Unterschiede in den Dateien haben jedoch keinen Einfluss auf unsere Beispiele. Also, falls Sie beim Testen unserer Beispiele eine ähnliche Fehlermeldung wie die folgende erhalten, dann läuft auf Ihrem Webserver kein PHP5.

Ein erstes Beispiel

An dieser Stelle könnte jetzt ein Anwendungsbeispiel erscheinen. Man könnte eine einfache Rechnung zu generieren – so es wie dieses Beispiel zeigt. Aber vielleicht haben Sie ganz andere Ideen…

Kategorien
Programmierung Sonstige Programmiersprachen

PHP und XML: Einfach einfach!

Die neue PHP-Version 5 enthält neben zahlreichen anderen Neuerungen auch die Erweiterung SimpleXML. Mit ihr kann XML auf erstaunlich einfache Art in ein PHP Objekt überführt und verarbeitet werden.

Release Candidate 3 von PHP5

artikel.xml:

 <?xml version="1.0" encoding="ISO-8859-1" ?>
  <artikel>
     <titel>Der Titel</titel>
     <teaser>Der Teaser, eine kurze Beschreibung</teaser>
     <inhalt>Der Artikelinhalt an sich</inhalt>
  </artikel>

simplexml_1.php:

      <?php

if (file_exists('artikel.xml')) {
   $xml = simplexml_load_file('artikel.xml');

echo $xml->teaser[0];

} else {
   exit('Konnte Datei nicht laden.');
}
?>

direkt in ein Objekt überführt

Dabei erfolgt der Zugriff auf die Elemente jeweils über Arrays. Da in unserem Fall nur ein Element innerhalb des Wurzelelementes <artikel> als <teaser> Element verwendet wird, geschieht der Zugriff auf den Inhalt dieses Elementes mit dem Arrayindex 0. Wären zwei <teaser> Elemente verwendet, würde man über den Index 1 auf das zweite Element zugreifen.

Um zu verstehen, wie bei SimpleXML die Daten in Objekten gespeichert werden, kann man sich mit der var_dump() Funktion von PHP schlicht den gesamten Inhalt des Objektes ausgeben lassen. Man ändert den Code einfach wie folgt ab:

simplexml_1.php:

      <?php

if (file_exists("artikel.xml")) {
   $xml = simplexml_load_file("artikel.xml"');

var_dump($xml);
} 
else {
   exit("Konnte Datei nicht laden. ");
}
?>

Die Ausgabe nach Aufruf des Skriptes sieht wie folgt aus:

      object(SimpleXMLElement)#1 (3) 
{ 
["titel"]=> string(9) "Der Titel" 
["teaser"]=> string(35) "Der Teaser, eine kurze Beschreibung" 
["inhalt"]=> string(25) "Der Artikelinhalt an sich" 
}

Wenn man bedenkt, dass zum Parsen eines XML Dokumentes mit dem ereignisorientierten Parser Expat mindestens drei Funktionen definiert werden mussten, ist der hierzu notwendige Aufwand mit SimpleXML nicht vergleichbar.

Komplexere XML Dokumente und SimpleXML?
Auch komplexe XML Dokumente lassen sich mit SimpleXML rasch bearbeiten. Folgendes XML Dokument ist fast identisch mit dem Beispiel oben, es wurde jedoch mit Attributen und weiteren Elementen versehen.

artikel_2.xml:

      <?xml version="1.0" encoding="ISO-8859-1" ?> 
 <content>
 <artikel id="1">
 <meta>
  <keywords>ein,netter,artikel</keywords> 
  <description>ein,lustiger,artikel</description> 
  </meta>
  <titel>Der Titel</titel> 
  <teaser>Der Teaser, eine kurze Beschreibung</teaser> 
  <inhalt>Der Artikelinhalt</inhalt> 
  </artikel>
 <artikel id="2">
 <meta>
  <keywords>ein,zweiter,netter,artikel</keywords> 
  <description>ein,zweiter,lustiger,artikel</description> 
  </meta>
  <titel>Der zweite Titel</titel> 
  <teaser>Der zweite Teaser, eine kurze Beschreibung</teaser> 
  <inhalt>Der zweite Artikelinhalt</inhalt> 
  </artikel>
  </content>

XPath Prozessor

Dies soll im folgenden Beispiel geschehen – es soll lediglich der vom Besucher angefragte Artikel angezeigt werden. Der gewünschte Artikel wird durch die ID identifiziert, die im <artikel> Element als Attribut definiert ist. Diese ID wird über den URL an das PHP Programm übergeben.

simplexml2.php

      <?php
      $id = $_GET['id'];
      if (file_exists("test.xml")) {
         $xml = simplexml_load_file("test.xml");
         $path ="/content/artikel[@id=".$id."]";
         if (!$res = $xml->xpath($path)) {
   echo "Artikel nicht vorhanden!";
   }
   else {
   echo "<h1>".$res[0]->titel."</h1>";
   echo "<p><b>".$res[0]->teaser."</b></p>";
   echo "<p>".$res[0]->inhalt."</p>";
   }
}
 else {
   exit("Konnte Datei nicht laden.");
}
?>

Artikel-Objektes

Screenshot
Das Ergebnis beim Aufruf mit id=1

Natürlich kann man ein solches Problem auch ohne XPath lösen, SimpleXML stellt die dazu notwendigen Funktionen zur Verfügung – doch bietet SimpleXML gerade durch die Schnittstelle zu XPath viele weitere Möglichkeiten für die dynamische Programmierung. SimpleXML sollte also nicht unterschätzt werden.

Weitere Informationen zu SimpleXML und PHP5.