Claudius L.: Variable in RewriteRule nutzen

Hallo zusammen!

Ich knoble nun schon seit einiger Zeit an einem Problem mit einer in einer RewriteRule zu nutzenden Variablen und hoffe auf eure Hilfe.

Mein Ziel ist, die von diesem Code gezeigte Funktionalität zu erreichen (wenn er denn funktionieren würde - dazu gleich).

RewriteEngine On

RewriteRule .* - [E=PAGES_DIRECTORY:page/]
RewriteRule ^%{ENV:PAGES_DIRECTORY}.*$ index.php

Mir geht es vor allem darum, PAGES_DIRECTORY nur ein Mal - in der .htaccess - definieren zu müssen und bei einer Änderung am Wert auch nur in einer Datei eine Änderung vornehmen zu müssen und nicht auch noch im verarbeitenden PHP-Skript. Dies hatte ich gehoft erreichen zu können, indem ich $_SERVER['PAGES_DIRECTORY'] bzw. $_SERVER['REDIRECT_PAGES_DIRECTORY'] verwende.
So viel zu dem, was ich gerne erreichen würde - nun zu den Problemen dabei:
Leider sind im Pattern einer RewriteRule keine Variablen erlaubt, weshalb mein Code oben nicht funktioniert (getestet mit Apache 2.2, "funktioniert nicht" heißt hier, dass nicht auf index.php weitergeleitet wird). Gleiches gilt leider für das CondPattern von RewriteCond, wo ebenfalls keine Variablen verwendet werden können.
Als Alternative hatte ich zunächst an Folgendes gedacht:

RewriteRule ^(page/).*$ index.php [E=PAGES_DIRECTORY:$1]

Und tatsächlich, das funktionniert wie gewünscht. Zwar hätte ich PAGES_DIRECTORY lieber "offensichtlicher" mit SetEnv oder [E=PAGES_DIRECTORY:page/] definiert, doch immerhin muss ich es so nur an einer Stelle tun.
Allerdings gibt es immernoch ein Problem: der Fall eines Requests auf / oder /index.php. In beiden Fällen greift meine RewriteRule nicht und die Variable wird daher natürlich auch nicht definiert. Und hier weiß ich leider nichtmehr weiter. Klar, ich könnte zusätzlich

RewriteRule .* - [E=PAGES_DIRECTORY:page/]

verwenden, aber dann bin ich ja wieder genau in der Situation, die ich vermeiden wollte: mehrere Stellen, an denen ich den Variableninhalt festlegen muss.
Nicht, dass ihr mich für furchtbar faul haltet - natürlich ist das kein besonders großer Aufwand und wäre ansich auch kein Problem. Aber ich bin auf der Suche nach einer sauberen Lösung und kann bisher einfach nicht akzeptieren, dass es keine bessere gibt, als an mehreren Stellen das gleiche von Hand zu schreiben …

Ich hoffe, verständlich erklärt zu haben, was ich erreichen möchte (und warum) und wo das Problem liegt. Danke schonmal für alle geisteserhellenden Antworten!

Viele Grüße,

Claudius

  1. Hi!

    Mein Ziel ist, die von diesem Code gezeigte Funktionalität zu erreichen (wenn er denn funktionieren würde - dazu gleich).

    Du beschreibst aber dein eigentliches Ziel nicht. Ist es, dass du in einem Hauptverzeichnis bist, dort eine .htaccess liegen hast und nun für ein Unterverzeichnis keine eigene .htaccess ablegen willst, sondern eine Regel dafür in der Hauptverzeichnis-.htaccess erstellen willst?

    Geht es nicht, in der Haupt-.htaccess nur die Variable mit dem Verweis auf das Pages-Verzeichnis abzulegen und im Pages-Verzeichnis selbst eine eigene .htaccess mit zu sich selbst relativen Regeln zu erstellen?

    Lo!

    1. Hallo dedlfix!

      Du beschreibst aber dein eigentliches Ziel nicht.

      Da hast du leider verdammt Recht und wie mir jetzt auffällt, hat meine erster Beispielcode noch nicht einmal dargestellt, was er eigentlich sollte. Ich wollte das Beispiel vereinfachen, habe es dabei aber versehentlich verändert.
      Also ganz von vorne:
      Mein eigentliches Ziel sind Clean URIs - alle Seiten werden von einer index.php in / generiert, die URIs sollen aber sauber und sprechend sein. Kommt eine neue Seite dazu, soll dafür keine Änderung in der .htaccess notwendig sein. Das schließt eine RewriteRule wie

      RewriteRule ^ersteseite|zweiteseite$ index.php

      aus, da neue Seiten in der .htaccess ergänzt werden müssten. Eine gierige RewriteRule halte ich auch für keine gute Idee, da somit kein direkter Zugriff mehr auf irgendwelche Unterverzeichnisse oder Dateien mehr möglich wäre.

      RewriteRule ^.*$ index.php

      (Die Möglichkeit, mittels RewriteCond auf -f etc. zu prüfen kenne ich, ich bevorzuge aber eine andere Lösung.)
      Aus diesen Gründen möchte ich ein Prefix für alle Seiten-IDs verwenden. Beispielsweise page/, das soll aber veränderbar sein (je nach Wunsch z.B. auf "view/", "v/" o.Ä.). Sprich, jeder Request auf /page/.* soll auf /index.php weitergeleitet werden. Daher dachte ich an eine .htaccess im DocumentRoot, mit

      RewriteRule page/.* index.php

      Ich vermute, ab hier habe ich den Sachverhalt im Eingangsposting erklärt: Dass ich statt "page/" eben eine Variable verwenden möchte, welche im PHP-Skript verfügbar ist, die RewriteRule dies aber (nicht direkt) zulässt usw.

      Ist es, dass du in einem Hauptverzeichnis bist, dort eine .htaccess liegen hast und nun für ein Unterverzeichnis keine eigene .htaccess ablegen willst, sondern eine Regel dafür in der Hauptverzeichnis-.htaccess erstellen willst?

      Wenn ich das richtig verstanden habe, dann ein Stückweit ja. Vor allem aber, weil das Unterverzeichnis garnicht existieren soll.

      Geht es nicht, in der Haupt-.htaccess nur die Variable mit dem Verweis auf das Pages-Verzeichnis abzulegen und im Pages-Verzeichnis selbst eine eigene .htaccess mit zu sich selbst relativen Regeln zu erstellen?

      Das ginge. Aber dann müsste das Verzeichnis /page/ ja existieren, darin eine .htaccess liegen, welche dann wiederum auf /index.php weiterleitet. Dann aber habe ich wieder die Situation, dass in in /.htaccess die Variable definiert werden muss und ich bei einer Änderung noch eine zweite Änderung vornehmen muss, nämlich das Verzeichnis /page umbenennen.

      Ich hoffe, dass ich habe dich richtig verstanden habe und jetzt deutlich machen konnte, worum es mir eigentlich ging/geht.
      Und natürlich – zum gefühlt x-ten Mal – herzlichen Dank für deine Antwort!

      Viele Grüße,

      Claudius

      1. Hi!

        Mein eigentliches Ziel sind Clean URIs

        OK, aber warum dann ein Bestandteil (page), der keine wirkliche Bedeutung hat, also die "saubere" URL "verunreinigt"?

        Jedenfalls hab ich nun dein Anliegen insoweit verstanden - nur keine Lösungsidee. Außer, ein Setup-/Config-Script zu schreiben, das die Änderung an all den Stellen einträgt, an denen das notwendig ist.

        Lo!

        1. Hey!

          Mein eigentliches Ziel sind Clean URIs

          OK, aber warum dann ein Bestandteil (page), der keine wirkliche Bedeutung hat, also die "saubere" URL "verunreinigt"?

          Wegen folgendem Beispiel:

          example.test/seiteeins
          example.test/seitezwei

          Diese beiden Anfragen sollen intern auf example.test/index.php weitergeleitet werden. Dazu fallen mir spontan zwei Möglichkeiten ein:

          RewriteRule ^seiteeins|seitezwei$ index.php
          RewriteRule .* index.php

          Beide Lösungen gefallen mir nicht.
          Die erste deshalb, weil, wenn irgendwann eine "seitedrei" dazukommt, diese in der .htaccess extra hinzugefügt werden muss. Hier käme ein von dir Erwähntes Config-Skript ins Spiel.
          Die zweite missfällt mir, weil dann auch example.test/img/image.jpg auf /index.php weitergeleitet wird. Und selbst wenn ich mit einer RewriteCond prüfe, ob eine existierende Datei oder ein Verzeichnis angefordert wird, so entstehen immer noch Probleme, wenn versehentlich mal eine Seite "img" angelegt und example.test/img  angefordert wird - da unklar ist, ob example.test/img ein Verzeichnis oder eine Unterseite ist.
          Deshalb meine Vorliebe für "verunreinigte" Clean URIs. Wenn ich festlege "alle meine Mehr-Oder-Weniger-Clean-URIs beginnen mit page/" und "page/ darf kein physikalisches Verzeichnis sein, zumindest keines, das per HTTP erreichbar sein muss", kann ich bedenkenlos alles, was ^page/.*$ matcht auf /index.php weiterleiten.
          Wichtig dabei ist, dass das PHP-Skript erfährt, wie der Clean-URI-Präfix (hier "page/") heißt, damit es den Request richtig verarbeiten kann.
          Mit meinem Ansatz

          RewriteRule ^(page/).*$ index.php [E=PAGES_DIRECTORY:$1]

          hatte ich dieses Problem beinahe gelöst, eben bis auf Requests, bei denen index.php aufgerufen wird, ohne das obige RewriteRule greift - also bei / und /index.php.

          Jedenfalls hab ich nun dein Anliegen insoweit verstanden - nur keine Lösungsidee.

          Schade, das ist neu. ;-)
          Ich hatte inzwischen selbst eine Idee, die jedoch auch nicht zum Ziel führte:

          RewriteRule ^($|index.php$|(page/).*)$ index.php [E=PAGES_DIRECTORY:$2]

          Erhofft hatte ich mir, dass dieses Pattern bei allen Requests greift, die entweder direkt auf / gehen, auf /index.php oder auf /page/.* und in jedem Fall REDIRECT_PAGES_DIRECTORY den Wert "page/" erhält. Leider ist das nicht der Fall. Beim Zugriff auf /index.php ist REDIRECT_PAGES_DIRECTORY nicht gesetzt, beim Zugriff auf / ist es ein leerer String.
          Fällt dir dazu vielleicht etwas ein?

          Außer, ein Setup-/Config-Script zu schreiben, das die Änderung an all den Stellen einträgt, an denen das notwendig ist.

          Das ist eine denkbare Lösung, die ich aber gerne vermeiden würde. Ich denke, im Vergleich dazu ist der Aufwand, "page/" an zwei Stellen zu schreiben, geringer …

          Viele Grüße,

          Claudius

          1. Hi!

            RewriteRule .* index.php
            Die zweite missfällt mir, weil dann auch example.test/img/image.jpg auf /index.php weitergeleitet wird. Und selbst wenn ich mit einer RewriteCond prüfe, ob eine existierende Datei oder ein Verzeichnis angefordert wird, so entstehen immer noch Probleme, wenn versehentlich mal eine Seite "img" angelegt und example.test/img  angefordert wird - da unklar ist, ob example.test/img ein Verzeichnis oder eine Unterseite ist.
            Deshalb meine Vorliebe für "verunreinigte" Clean URIs. Wenn ich festlege "alle meine Mehr-Oder-Weniger-Clean-URIs beginnen mit page/" und "page/ darf kein physikalisches Verzeichnis sein, zumindest keines, das per HTTP erreichbar sein muss", kann ich bedenkenlos alles, was ^page/.*$ matcht auf /index.php weiterleiten.

            Du willst also Probleme, die durch Unachtsamkeit beim Benennen von Verzeichnissen, die sich dann mit verwendeten URLs beißen, entstehen, lösen. (Schön geschachtelt, nur leider ein bisschen kurz.) Dabei nimmst du einen Lösungsansatz, der per se nicht besonders schön ist und gerätst dabei in weitere Probleme, die sich gar nicht bis nur unschön lösen lassen. Ich würde ja die ganzen dabei entstehenden Komplex- und Kompliziertheiten auflösen, indem ich die bewährte -d/-f/.*-Lösung nähme und bei der Benennung von Verzeichnissen aufpasste. Dein img-Verzeichnis für die statischen Bilder der Seite müsste nicht so prägnant und kurz benannt sein. Das kann ruhig etwas komplexer und mit weniger "Könnte-später-mal-verwendet-werden-müssen"-Gefahr benannt sein. Nenn das doch einfach /page/img/ (und /page/css/ etc.), dann ist es aus dem Weg. Wie die URLs zu eingebetteten Ressourcen aussehen, interessiert doch im Grunde niemanden. Und eine URL /page als eine Seite deiner Anwendung im Namen wird's wohl nicht geben.

            Lo!

            1. Hey!

              Naja, ich betrachte das glaube ich von einem etwas anderem Blickwinkel.
              Zunächst: Die Pfade zu eingebetteten Ressourcen sind mir tatsächlich egal und können meinetwegen beliebig kryptisch sein. Allerdings war /img/ nur als Beispiel gemeint.
              Du hast recht, man kann und sollte sich beim benennen von Verzeichnissen Gedanken machen und kann so im Prinzip Probleme vermeiden. Aber das alleine reicht noch nicht. Ich würde gerne einen Mechanismus schaffen, bei dem ohne Kenntnis irgendwelcher Verzeichnisstrukturen durch ein webbasiertes Konfigurationsinterface neue Unterseiten hinzugefügt werden können. Und dazu ist mein "unschöner" Ansatz mit dem Präfix vor den "clean" URIs die bisher einzige Lösung, die mir einfällt.
              Auch mit einer RewriteCond, die auf -f und -d prüft, erhalte ich nicht die gleiche Fehlersicherheit, da über das Webinterface eine Unterseite erstellt werden könnte, die später nicht aufgerufen werden kann (weil ihre URI mit einer existierenden Datei oder einem Verzeichnis kollidiert).

              Viele Grüße,

              Claudius