PHP saubere Url's mit htaccess

_CH_K_1991_

Lieutenant
Registriert
Nov. 2008
Beiträge
772
Hallo zusammen

Ich habe mich kürzlich mal an .htaccess Dateien gewagt, weil ich gerne saubere Url's für eine Website haben möchte.
Nur leider klappt dies noch nicht vollständig.

folgendes wäre das Ziel:
[TABLE="width: 750"]
[TR]
[TD]URL's mit Files im Root[/TD]
[TD]URL's mit Files in einem Ordner (erster Teil)[/TD]
[/TR]
[TR]
[TD]page.com/about[/TD]
[TD]page.com/products/product1[/TD]
[/TR]
[TR]
[TD]page.com/products[/TD]
[TD]page.com/products/product2[/TD]
[/TR]
[/TABLE]

Was bisher funktioniert:
-> Alle Pages welche im Root-Folder liegen werden korrekt aufgerufen und auch dargestellt.

Was noch nicht funktioniert:
-> Pages welche in einem Ordner liegen (welcher gleich heisst wie eine page) funktioniert nicht korrekt.
-> falsch eingegebene URI's möchte ich auf eine 404 Seite umleiten.

folgender Code ist die .htaccess Datei welche ich bisher erstellt habe:
Code:
ErrorDocument 404 /error404.php


RewriteEngine On
RewriteBase /
RewriteRule ^([a-zA-Z0-9]+)$ index.php?page=$1
RewriteRule ^([a-zA-Z0-9]+)/([a-zA-Z0-9]+)$ index.php?page=$1/$2

und das wäre die index.php:
PHP:
<?php    $website = "http://page.com";
$startpage = "home.php";
$head = "additional/head.php";
$header = "additional/header.php";
$footer = "additional/footer.php";
$sites = array("home", "about", "products");
if(empty($_GET['page'])){
  include($head);
  include($header);
  include($startpage);        
  include($footer);}
elseif (!empty($_GET['page'])){
  $site = $_GET['page'];
  /*über diese Abfrage möchte ich falsch eingegebene URI's auf die Startseite umleiten oder in ein 404 Dokument*/
  if (in_array("$site", $sites)){
    $page = $_GET['page'] . ".php";
    include($head);
    include($header);
    include("$page");
    include($footer);
  }
else {
  include($head); 
  include($header);
  include($startpage);
  include($footer);
}
}
else
{
header("Location: $website");
}?>


Vielen Dank für die Hilfe.
Gruss Matthias
 
Zuletzt bearbeitet:
Hier einmal der PHP-Code formatiert, das kann ja keiner lesen:
PHP:
<?php
$website = "http://page.com";
$startpage = "home.php";
$head = "additional/head.php";
$header = "additional/header.php";
$footer = "additional/footer.php";
$sites = array("home", "about", "products");

if(empty($_GET['page']))
{
	include($head);
	include($header);
	include($startpage);
	include($footer);
}
elseif(!empty($_GET['page']))
{
	$site = $_GET['page'];

	/*über diese Abfrage möchte ich falsch eingegebene URI's auf die Startseite umleiten oder in ein 404 Dokument*/
	if(in_array("$site", $sites))
	{
		$page = $_GET['page'].".php";

		include($head);
		include($header);
		include("$page");
		include($footer);
	}
	else
	{
		include($head);
		include($header);
		include($startpage);
		include($footer);
	}
}
else
{
	header("Location: $website");
}
?>

Edit: Ah, jetzt hat der Autor ihn selbst angepasst... Zu spät gesehen.
 
danke, hab ihn noch ein bisschen angepasst, ich weiss auch nicht wieso das kopieren von Sublime in den Chrome so ein Chaos veranstaltet hat.

EDIT:// ;)
 
Du darfst nicht / zwischen zwei Parameter schreiben.

RewriteCond %{REQUEST_URI} !-d
RewriteCond %{REQUEST_URI} !-f
RewriteRule ^([a-zA-Z0-9_]+)(/([a-zA-Z0-9_]+))$ /index\.php?s=$1&us=$3 [L]
RewriteRule ^([a-zA-Z0-9_]+)$ /index\.php?s=$1 [L]


So sieht das bei mir aus, s bzw us musst du halt anpassen.
 
Viel eleganter: arbeite nicht mit separaten .php - Dateien pro Unterseite (und erst recht Ordnern), sondern rein über eine Datenbank, numerischen IDs sowie Alias-Strings. Dann gibts eben meinetwegen n Alias namens "sub/subpage".
 
Er will doch alles auf die index.php mit Parametern umleiten, wie bei mir auch.
Ob er dann mi Hilfe der Parameter eine komplette Site/nur den Inhalt aus einer .php Datei holt oder aus der Datenbank macht für das URL Rewriting ja nicht unbedingt nen Unterschied.

Numerisch ist leichter zu prüfen, man kann die Parameter aber auch gegen ne art whitelist checken oder warum meinst du?
 
Doch, es ist deutlich einfacher, wenn man in einer Datenbank für jede Seite sowohl eine numerische ID als auch einen Text-Alias hat.

index.php?page=seite1/unterseite11 (wobei "seite1/unterseite11" der gesamte Alias ist) lässt sich recht leicht zu seite1/unterseite11.html umschreiben
 
Achso ich hab übersehen, das das gar nicht zwei Parameter sind sry., vergiss das mit dem /.
Nochmal genauer angeschaut...

Bspw. "product/product1" ist nicht in dem Array $sites, deshalb geht es weiter zum else... kann das sein?
 
Zuletzt bearbeitet:
Guten Abend zusammen

Danke für eure Antworten.
@Znrl
Danke für dein Code Bsp.
Hierzu habe ich noch eine Frage:
Bei der zweitletzten Zeile (RewriteRule ^([a-zA-Z0-9_]+)(/([a-zA-Z0-9_]+))$ /index\.php?page=$1&us=$3 [L]) gibst du als erstes ja normal die page mit (habe es bereits umbenannt) und dann dahinter noch einen möglichen Parameter für sonstige Angaben. Nur wie wähle ich diesen denn aus resp, für was ist denn der genau?
Denn es funktioniert jetzt wirklich alles, ausser eben der Aufruf einer File in einem Ordner. Ich habe folgendes getestet:
Wenn ich page.com/product/product1 mitgebe, list mir meine index.php nur product aus der GET Variable und der zweite Parameter wird einfach weggeschnitten. Daher leitet die index.php die Anfrage einfach auf meine Startseite weiter (wie ich es programmiert habe, da es die Seite productS so nicht gibt).
Muss ich das irgendwie anders angeben? Oder muss ich eine weitere htaccess Zeile erfassen?
PS: die Seite ist auf der Whitelist.
Danke

@Daaron
Klar wäre es eine möglichkeit die ganze Website aus einer Datenbank auszuwählen, nur ich finde der Aufwand den Code zu pflegen wäre doch deutlich höher als wenn ich diesen einfach per Ajaxplorer z.B. kurz anpassen kann (eine Information z.B.) als wenn ich die ganze Website noch lokal haben muss um den Code zu testen und dann in die Datenbank zu laden.
Oder gibt es da irgendwelche Tools die das auch gerade automatisch machen?!?
Wäre gut wenn du das noch näher erläutern könntest.
Danke

Aktuelle .htaccess:
Code:
RewriteEngine On
RewriteEngine OnRewriteCond %{REQUEST_URI} !-d
RewriteCond %{REQUEST_URI} !-f
RewriteRule ^([a-zA-Z0-9_]+)(/([a-zA-Z0-9_]+))$ /index\.php?page=$1&us=$3 [L]
RewriteRule ^([a-zA-Z0-9_]+)$ /index\.php?page=$1 [L]
PS: @Znrl, weiss nicht genau welches Slash (siehe dein vorheriger Post) du weglassen möchtest...
 
Zuletzt bearbeitet: (Code ergänzt)
Ich hatte zuerst zu schnell drüber geschaut und gedacht du hast zwei Paramter.

Deine Rewrite Rule funktioniert, da du "products/product" in einem Parameter hast.

RewriteEngine On
RewriteBase /
RewriteRule ^([a-zA-Z0-9]+)$ index.php?page=$1
RewriteRule ^([a-zA-Z0-9]+)/([a-zA-Z0-9]+)$ index.php?page=$1/$2

Also wenn ich das bei mir mache und einfach nur


PHP:
$seite = $_GET["page"];

echo $seite;

ausgeben lasse

kommt beim aufruf page.com/products (intern dann zu page.com?page=products)
-> products
bei page.com/products/product1 (intern dann zu page.com?page=products/product1)
-> products/product1

Bei deinem Code oben hast du nur $sites = array("home", "about", "products");

Das heißt, products/product1 gibts nicht (oder hast du das bei dir drin?).

Wenn das bei dir nicht so ist, hast du wohl ein Problem mit dem "/". Möglich, dass das bei dir anders encodiert wird und dann nichtmehr Parameter gezählt wird sondern als Trenner o.ä.
Mit "\%2F" statt nur "/" hat es bei mir auch funktioniert, u.U. noch urldecode() auf die per $_GET erhaltenen Variablen anwenden.
$seite = urldecode($_GET["page"]);

Ich habs mit dem IE 11, Firefox und in Xampp auf Windows 8.1 getestet.


Naja ein "Tool", das das macht heißt CMS. Sprich Drupal, Joomla, Contao, ...
Was letztendlich mehr Aufwand ist hängt davon ab wie dus benutzt. Eine lokale Version seiner Website macht aber zum testen/als Backup immer Sinn.

edit: RewriteRule ^([a-zA-Z0-9]+)/([a-zA-Z0-9]+)$ index.php?page=$1\%2F$2
 
Zuletzt bearbeitet: (Rechtschreibung/Formatierung)
du kannst deinen code wesentlich schlanker gestallten.

ungetestet:
PHP:
<?php
$website = "http://page.com";
$startpage = "home.php";
$head = "additional/head.php";
$header = "additional/header.php";
$footer = "additional/footer.php";
$sites = array("home", "about", "products");

include ($head);
include ($header); 

if (empty ($_GET['page'])) {
	include ($startpage);
} elseif (!empty ($_GET['page'])) {
	$site = $_GET['page'];
	/*über diese Abfrage möchte ich falsch eingegebene URI's auf die Startseite umleiten oder in ein 404 Dokument*/
	if (in_array ("$site", $sites)) {
		$page = $_GET['page'].".php";
		include ("$page");
	} else {
		include ($startpage);
	}
} else {
	header ("Location: $website");
}
include ($footer);
?>
 
_CH_K_1991_ schrieb:
Klar wäre es eine möglichkeit die ganze Website aus einer Datenbank auszuwählen, nur ich finde der Aufwand den Code zu pflegen wäre doch deutlich höher als wenn ich diesen einfach per Ajaxplorer z.B. kurz anpassen kann (eine Information z.B.) als wenn ich die ganze Website noch lokal haben muss um den Code zu testen und dann in die Datenbank zu laden.

Du lädst doch nicht den PHP-Code in die Datenbank (z.B. als BLOB), das wäre ziemlicher Mumpitz. Spätestens wenn der Hoster exec() zur Sicherheit abgeschaltet hat würde dann gar nichts mehr gehen, außerdem hättest du keine Möglichkeit zu debuggen und OpCode-Caching wäre auch unmöglich.

Nö, du legst dir im einfachsten Fall einfach einen Datenbankeintrag an: ID, Alias, Pfad-zur-PHP-Datei

Wenn du das dann noch mit ein paar weiteren Spalten erweiterst kannst du aus dieser DB sogar noch viel mehr machen. Du kannst z.B. aus 1-2 Tabellen eine Navigationsstruktur zusammenbauen. Du kannst <title>, Meta-Description/Keywords oder andere Head-Tags (z.B. OpenGraph oder ICBM) verwalten,...
 
Wenn du schon den Code verbesserst würde ich auf jedenfall isset vor oder anstelle von empty nehmen (zumindest in der ersten if), sonst versuchst du bei jedem Zugriff auf die Seite ohne Parameter auf ein undefined index in $_GET zuzugreifen.
Das wird dir auch gemeldet, wenn du das error reporting auf ALL stellst.

edit:
Jetzt weiß ich glaub cih auch auch was Daaron genau meint, stand etwas auf dem Schlauch...
Das geht auch in ner "eigenen Datenbank" z.B. pages.php mit mehrdimensinalen Arrays oder mehreren Arrays kann man zu jeder Seite Sachen wie Titel, Pfad, angezeigter Name in der Navigation speichern.
(Oder eben mySQL etc.)

Dann kannst du die Seite viel dynamischer Aufbauen und <title>, <meta> etc. was du halst brauchst abhängig von der aktuellen Seite abrufen:

Du suchst eben dann ob $_GET['page'] im Array ist, wenn ja holst du dir die ID dazu.
Mit ID, bei der Startseite ID = 0
$titel = $seiten["title"][$id];
ist dass der Titel an erster Stelle im Titel Array
steht dann in deinem $head bspw.:
<title><?php echo ($titel); ?></title>

Dann kannst du auch nen Menü-Array erstellen und mit ner Schleife dein Menü automatisch aufbauen lassen und aus den Arrays/Datenbank die Links (Pfade) und die Namen (Pfad: page.com, Name: Home/Startseite) er Links etc. holen.

edit2: @ unter mir... Ich hab das error reporting lokal im XAMPP an, nicht auf dem Webserver.
 
Zuletzt bearbeitet:
Statt Error Reporting zu aktivieren (was die Seite zerballern kann und Sicherheitslücken veröffentlicht) sollte man einfach ne Shell nebenher laufen lassen, in der man (sinngemäß) "tail -f error.log" ausführt.
 
Danke euch allen.

@Blackbenji
Jap da hattest du recht. Danke für deine Korrektur.

@Daaron
Jetzt verstehe ich wie du es meinst. Ich habe mir eben schon beim ersten Mal lesen gedacht, da sagen immer alle man solle z.B. Bilder nicht in eine DB speichern, sondern nur die Pfade und jetzt will er Code in die DB speichern... :D
Es ist wirklich eine gute Idee. Ich werde diese mal bei einem kleineren Projekt ausprobieren.
Das Problem bei der Webentwicklung ist, dass immer wenn man meint jetzt habe man gerade wieder etwas großartiges gelernt, schon bald wieder etwas neues dazukommt...
Naja so bleibt das Ganze doch wenigstens spannend ;-)

@Znrl
Danke für deine Korrektur, so funktioniert es jetzt auch mit dem Unterordner.
Nur vielleicht hast du jetzt noch eine Idee für folgendes Problem, was jetzt logischerweise auftritt.
Wenn ich die Seite product1 generiere (also zusammensetzte) muss ich einen eigenen Head für die Unterordner Files haben oder per PHP je nach URL den richtigen Head zusammenstellen. Wenn ich jetzt aber Bilder im CSS vergebe geht das ja nicht so einfach. Wie könnte ich denn das in einem CSS lösen?
 
_CH_K_1991_ schrieb:
da sagen immer alle man solle z.B. Bilder nicht in eine DB speichern, sondern nur die Pfade
Kann unter Umständen tatsächlich sogar Sinn machen. Zugriffsrechte auf Datenbankfelder lassen sich noch besser regulieren als auf Dateien.

Das Problem bei der Webentwicklung ist, dass immer wenn man meint jetzt habe man gerade wieder etwas großartiges gelernt, schon bald wieder etwas neues dazukommt...
Dann spar ich dir viel Zeit: Die Endlösung heißt fast immer Open Source CMS... und zwar eins mit guter, sauberer und schneller Codebasis

muss ich einen eigenen Head für die Unterordner Files haben oder per PHP je nach URL den richtigen Head zusammenstellen. Wenn ich jetzt aber Bilder im CSS vergebe geht das ja nicht so einfach. Wie könnte ich denn das in einem CSS lösen?
Ohne groß darüber nachzudenken: Nimm absolute Pfade...
 
Das muss du mir jetzt erklären.

Ich kenn dein $head und deine Stylesheets nicht. Was genau meinst du, das logischerweise nichtmehr funktioniert? Bzw. Was soll sich ändern?

edit: achso ja den Pfad vom root aus mit /pages/file.php also mit slash beginnen.
Ohne slash am Anfang fängt der Pfad im aktuellen Verzeichnis an.
Musst halt schauen, das hängt auch vom Server ab etc. wie das genau aussieht.
Gibt sonst auch in PHP Sachen wie:
$_SERVER["DOCUMENT_ROOT"]
 
Zuletzt bearbeitet:
ich hab mir dafür vor einiger zeit mal was geschrieben, bin mir grad aber nicht sicher ob es nicht noch einfacher geht:

PHP:
function save_dirname($path)
{
    $dirname = dirname($path);
    return $dirname == '/' ? '' : $dirname;
}

$WEBSERVER = 'http://' . $_SERVER['SERVER_NAME'] . save_dirname($_SERVER['PHP_SELF']);
$WEBSERVER lässt sich dann schön im template nutzen ala
PHP:
<?php echo $WEBSERVER; ?>/template/defaut/image/bild.jpg
PHP:
<?php echo $WEBSERVER; ?>/template/defaut/style/main.css
 
​hehe :) sorry
Ich habe wohl ein durcheinander gemacht... mit dem / voran klappt es logischerweise.
Danke für eure Hilfe.
Ergänzung ()

Eine Frage hätte ich noch...
Wie mache ich jetzt das am besten mit den Error Documents?

In der .htaccess habe ich jetzt vor der RewriteEngine On Zeile folgende beiden Zeilen drin:

ErrorDocument 403 /error/error403.php
ErrorDocument 404 /error/error404.php

Wenn es jetzt zu z.B. zu einem 403 kommt, wird oft immernoch die Browser eigene Fehlerseite angezeigt mit der zusätzlichen Meldung:

Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

Das heisst ja, dass der meine .htaccess probiert die ErrorDocuments aufzurufen aber den Pfad nicht findet, richtig?

Wie kann ich jetzt das Ganze noch umstellen, damit dieser die ErrorDocuments im Error Ordner findet?
Wenn ich nämlich die ErrorFiles in den RootOrnder pflanze, findet er diese ohne Problem.

​Danke und Gruss
 
Zurück
Oben