Karl: richtige includes in php

Hallo, ich baue nun schon seit ein paar Monaten php Seiten und bin gerade mal wieder über das Thema includes bzw. require_once gestolpert.

Bisher habe ich Konfigurationen oder andere PHP Dateien immer so mit relativen Pfaden in meine Skripte includiert:


//Konfiguration
require_once ("../config/baseconfig.php");
   
//DB-Verbindungsdaten
require_once("connections/db_connection.php");

Das klappt auch prima unter Windows wie unter Linux, allerdings soll das so nicht sicher sein habe ich gelesen. Nur verstanden habe ich das noch nicht so ganz. Ein Beispiel im Netz sagte ich könnte das so machen:


//Konfiguration
require_once dirname(__FILE__).("/../config/baseconfig.php");
   
//DB-Verbindungsdaten
require_once dirname(__FILE__).("/connections/db_connection.php");

O.K. klappt auch, nur was ist daran besser? Wäre doch ganz schön aufwändig alle Scripte zu ändern...

Danke + Gruß Karl

  1. Tach!

    Das klappt auch prima unter Windows wie unter Linux, allerdings soll das so nicht sicher sein habe ich gelesen. Nur verstanden habe ich das noch nicht so ganz.

    Was stand denn als Begründung dabei?

    O.K. klappt auch, nur was ist daran besser?

    Der Unterschied zwischen deiner Version und dem Voschlag ist, dass deine Version vom aktuellen Arbeitsverzeichnis ausgeht. Das kann geändert werden. Wenn das aber nicht getan wird, gibt es keinerlei Probleme mit der relativen Adressierung. Der Vorschlag hingegen erzeugt eine absolute Pfadangabe, basierend auf dem Verzeichnis der aktuellen Datei und ist somit immun gegen einen eventuellen Arbeitsverzeichniswechsel.

    Eine andere Möglichkeit ist, einen Pfad außerhalb des Documentroot speziell für solche Dateien zu erstellen, die nicht direkt über eine URL aufgerufen werden. Diesen kann man mit include_path bekanntgeben, und alle relative Adressierung sucht die Dateien in den dort angegebenen Pfaden.

    dedlfix.

    1. Danke für die Antwort.

      Der Vorschlag hingegen erzeugt eine absolute Pfadangabe, basierend auf dem Verzeichnis der aktuellen Datei und ist somit immun gegen einen eventuellen Arbeitsverzeichniswechsel.

      Warum bin ich damit immun? z.B. meine Verzeichnisstruktur:

      root
      -config
      -verzeichnis1
      --subverzeichnis1
      -verzeichnis2
      
      

      Wenn mein Script in "verzeichnis1" liegt, binde ich eine Datei aus dem Verzeichnis "config" so ein:

      require_once dirname(__FILE__).("/../config/baseconfig.php");
      

      Wenn ich mein Script nun in "subverzeichnis1" verschieben möchte, müsste ich den Pfad doch genauso anpassen?:

      require_once dirname(__FILE__).("/../../config/baseconfig.php");
      

      Und wenn ich die gesamte App in ein anderes Verzeichnis verschiebe stimmen die relativen Pfade doch auch weiterhin ohne dirname(FILE) ?

      1. Hallo Karl,

        wenn Du die innere Struktur deines Projekts veränderst (sprich: ein Script aus Ordner A nach Order B verlegst), dann hast Du immer Anpassungsaufwand, ganz gleich, ob Du dirname(__FILE__) vorschaltest oder nicht.

        Der Sinn der Vorschalterei ist ein anderer: Es ist Mikrooptimierung, und der Versuch, Mehrdeutigkeiten zu verhindern. Im Handbuch steht:

        Files are included based on the file path given or, if none is given, the include_path specified. If the file isn't found in the include_path, include will finally check in the calling script's own directory and the current working directory before failing.

        "none is given" gilt auch für ein include "foo/bar.inc";. Bevor PHP im Script-Verzeichnis oder im aktuellen Verzeichnis sucht, probiert es zunächst den include_path aus. Darin kann natürlich ein "." enthalten sein, das muss aber nicht sein. Wenn es foo/bar.inc bei Dir mehrfach gibt, hängt es an der Reihenfolge der Einträge im include_path, welches davon gefunden wird.

        Zweites Problem: Wenn deine vom Browser aufgerufenen Scripte nicht nur im Web-Root stehen, sondern in diverse Unterverzeichnisse verteilt sind, dann wechselt das aktuelle Verzeichnis je nach aufgerufenem Script. Denn das aktuelle Verzeichnis ist immer das, in dem das Script steht, für das der Web-Request ausgeführt wird. Code, der einfach nur mit "." und ".." navigiert, gerät dadurch in Verwirrung.

        dirname(__FILE__) verschafft Dir einen sicheren Bezugspunkt, relativ zu der Datei aus der der Include gemacht wird.

        Zwingend nötig ist es aber nicht. Es hängt ganz an der Komplexität und an der Struktur deines Projekts. In einem älteren Projekt von mir hatte ich es noch anders gemacht: da habe ich eine globale Variable angelegt, in der der Bezugsordner für Includes stand, und alle Includes darauf basiert. Elegant war das auch nicht. Aber ich hatte dadurch die Option, Sourcecode in andere Ordner zu verschieben, ohne die Pfade zu den Includes anpassen zu müssen. An den include_path hatte ich damals nicht gedacht, da war ich noch zu grün hinter den Ohren.

        Rolf

        --
        sumpsi - posui - obstruxi
      2. Tach!

        Der Vorschlag hingegen erzeugt eine absolute Pfadangabe, basierend auf dem Verzeichnis der aktuellen Datei und ist somit immun gegen einen eventuellen Arbeitsverzeichniswechsel.

        Warum bin ich damit immun?

        Es geht nicht darum, dass du die Dateien verschiebst. In dem Fall ist natürlich eine Änderung vorzunehmen, die die neue Lage abbildet. Es geht bei dem absoluten Pfad darum, dass ein chdir() zur Laufzeit das Arbeitsverzeichnis ändert und damit die relativen Angaben sich auf das neue Arbeitsverzeichnis beziehen. Das ist jedenfalls dann relevant, wenn die Datei nach dem Wechsel inkludiert wird.

        Wie gesagt, wenn du (oder Scripts von Drittherstellern) kein chdir() machst, dann brauchst du dagegen keine Vorkehrungen einzubauen. Aber andere Situationen verlangen andere Vorgehensweisen. Deswegen ja auch meine erste Frage, welche Begründung dem Vorschlag zugrundelage, also welches Problem er zu lösen versuchte. Daraus leitet sich ab, ob das für dich nützlich ist oder nicht.

        dedlfix.

  2. Lieber Karl,

    //Konfiguration
    require_once dirname(__FILE__).("/../config/baseconfig.php");
    

    ich verwende __DIR__ als magische Konstante:

    require_once __DIR__.'/path/script.php';
    

    nur was ist daran besser?

    Du bist sicherer gegen die Verwendung von chdir() als ungeahnte Fehlerquelle.

    Wäre doch ganz schön aufwändig alle Scripte zu ändern...

    Wenn Du damit vorhandene Fehler/Bugs reparieren kannst, warum nicht? Und gute Editoren können prima suchen und ersetzen - über Dateien in Verzeichnissen hinweg...

    Liebe Grüße

    Felix Riesterer

  3. 
    //Konfiguration
    require_once dirname(__FILE__).("/../config/baseconfig.php");
       
    //DB-Verbindungsdaten
    require_once dirname(__FILE__).("/connections/db_connection.php");
    
    

    O.K. klappt auch, nur was ist daran besser?

    Nichts. Schlauer wäre [$_SERVER'DOCUMENT_ROOT'] zu verwenden

    //Konfiguration
    require_once $_SERVER['DOCUMENT_ROOT'] . '/config/baseconfig.php';
    require_once $_SERVER['DOCUMENT_ROOT'] . '/connections/db_connection.php';
    

    Wenn jetzt Deine Datei in ein anderes verzeichnis verschoben wird - der Pfad zur Ressource bleibt konstant.

    1. Hallo raketenwilli,

      Schlauer wäre [$_SERVER'DOCUMENT_ROOT'] zu verwenden

      Sehe ich in dieser Absolutheit nicht so. Bei einer zusammengehörigen Dateiengruppe, die als Block irgendwohin kopiert oder geschoben wird (Stichwort: PHP Library), ohne dass man festlegen möchte, wo im Verzeichnisbaum des Webs sie liegt, ist das kontraproduktiv.

      Man muss die Vor- und Nachteile der diversen Techniken kennen und je nach Situation die richtige verwenden.

      Rolf

      --
      sumpsi - posui - obstruxi
  4. Hi,

    ich kann dir nur wärmstens empfehlen, dich an die gängigen (Community-) Standards bzgl. Verzeichnisstrukturen und Includes zu halten. PHP: The Right Way, darin insb. PSR-4, sollten dir Anhaltspunkte geben. Auch folgender Link könnte dir helfen, ein paar der Grundkonzepte hinter modernem Code zu verstehen: https://symfony.com/doc/current/introduction/from_flat_php_to_symfony.html

    VG Matti