PHP und symbolische Links

Immer wieder stellt es mir ein Bein: PHP und symbolische links -.-. Kurzer Hintergrund:
Ich arbeite an einer Seite die verschiedene PHP-Module verwendet. In der lokalen Entwicklungsumgebung stelle ich diese Scripte dann mittels symbolische Links meinem xampp zur Verfügung. Das Problem an der ganzen Geschichte ist jetzt aber das PHP diesen symbolischen links folgt und dadurch meine Pfade in der Entwicklungsumgebung natürlich überhaupt nicht stimmen. Da Eiere ich schon einige Zeit drum rum und finde auch im Web keine sinnvolle Lösung (alles was ich finde dreht sich dann immer nur um den Apache und followSymlinks was mich nicht weiterbringt).

Habt ihr eine Idee, wie ich das abstellen kann?

Hast mal Beispiele von wo die Links eingebunden werden und wohin? Also wie die Verzeichnisstruktur bei dir aussieht.

Server und Sourcen liegen auf unterschiedlichen Partitionen, aber das kann man auch einfacher haben:

Pfade (Ordner):
htdocs\example
htdocs\example\files
htdocs\sourcen

symlink von sourcen nach example: mklink /D sourcen ..\sourcen (diesen Befehl in der Konsole im Ordner example ausführen)

in sourcen hat man jetzt eine php-Datei mit folgendem Inhalt:

<?php
// example.php
$handle = opendir("../files");
?>

Diese dann aufrufen über: http://localhost/example/sourcen/example.php

und das Ergebnis:


Warning: opendir(../files,../files): Das System kann die angegebene Datei nicht finden. (code: 2) in C:\xampp\htdocs\sourcen\example.php on line 2

Warning: opendir(../files): failed to open dir: No such file or directory in C:\xampp\htdocs\sourcen\example.php on line 2

Mein Workaround sieht momentan so aus:

class Util {
    static function _ignoreSymLink($path, $levelBack = 0) {
        $pfad = dirname($_SERVER['SCRIPT_FILENAME']);

        for($i = 0; $i < $levelBack; $i++)
            $pfad = dirname($pfad);

        return $pfad . "/" . $path;
    }

    static function ignoreSymLink($path) {
        $levelBack = 0;
        return Util::_ignoreSymLink(str_replace("../","",$path,$levelBack), $levelBack);
    }
}

was ich aber ziemlich umständlich finde … vor allem weil ich damit ein Problem löse, was ich später auf dem Server garnicht mehr haben werde -.-

ist wahrscheinlich eine Sicherheitsvorkehrung von PHP um nicht zufällig einen Link auf Root bereit zu stellen. Aber schön wäre es in der Tat wenn man das über die Config in PHP abschalten könnte - konnte dazu aber auf die schnelle Nix finden

Hmm, hier ist es eigentlich ein umgekehrter Fall. Das File in sourcen weiß nicht, dass es über einen symlink geöffnet worden ist. D.h ausgehend von source/example.php ist ein …/ das Verzeichnis htdocs (Sieht man eh schön an der Fehlemeldung wo die wirkliche Datei genannt wird). Dh. du müsstest im htdocs Verzeichnis einen symlink auf files erstellen.
@mogel Sicherheitsvorkehrung (open_basedir) muss man in PHP eher erst aktivieren.

Ansonsten sage ich immer, plain PHP (trotz Namespaces und Traits) ist Schwachsinn. Man sollte immer den Einsatz eines Frameworks in Erwägung ziehen. Erst dann ist auch PHP eine gute Sprache. Oder zumindestens bestimmte Komponenten und Bibliotheken einsetzen. Was solche Dateisystemgeschichten angeht, schau dir die Symfony Filesystem Komponenten an.

Aber dann sollte es doch genau umgekehrt sein, also dass er den symbolischen Link eben nicht auflöst.
@TheDarkRose : hmm das schaue ich mir an. Aber vieles sind unabhängige Kleinigkeiten für die mir das ein bisschen übertrieben vorkommen. Da einiges auf fertigen CMS-Systemen basiert könnte ich auch da mal nachschauen, was die so bieten. Die Idee werde ich auf jeden Fall mal weiter verfolgen.

Achja, opendir kann nicht mit symlinks umgehen. scandir sollte das können. Hmm meinte nicht das du Symfony als ganzes einsetzen sollst. Aber alle Komponenten des Frameworks sind auch einzeln einsetzbar.

dann habe ich das falsch Verstanden - PHP entzieht sich sowieso meinem Gehirn ^^

ne scandir kann es nicht.

Aber ein weiteres Problem was sich stellt sind eben auch Sachen wie require_once:


<?php
require_once "../xyz.php"; // befindet sich im Ordner example
?>

Warning: require_once(../xyz.php): failed to open stream: No such file or directory in C:\xampp\htdocs\sourcen\example.php on line 2

Hier das selbe, sieh dir die Fehlermeldung an. Das File liegt in sourcen und somit ist ein …/ der Ordner htdocs. Warum aber eigentlich die zwei verschiedenen Ordner?

Die Ausgangssituation ist aber nach wie vor die gleiche wie in Post #3 beschrieben. Würde PHP den symbolischen Link nicht auflösen, dann würde er die Datei finden. Die Unterschiedlichen Speicherorte haben z.T. etwas mit dem Projektmanagment zu tun und daran lässt sich leider nichts mehr ändern. Also im Idealfalle sollte PHP eigentlich mit folgender Struktur arbeiten:


example
 |-sourcen  (<-- hier der symbolische Link)
 |     |- example.php
 | - xyz.php

PHP löst hier gar nicht mehr auf. Die example.php weiß nicht dass es durch den symlink (durch den Webserver) aufgerufen wurde. Es kennt nur seine Position im Dateisystem. Und daher resultiert ein relatives ../ in den Ordner htdocs.

Man kann dies durch ein blödes Konstrukt umgehen.

define('BASEDIR', $_SERVER['DOCUMENT_ROOT'] . '/example/');

requirce_once(BASEDIR . 'xyz.php');

Ist aber auch ziemlich dirty. Hier wird der Vorteil des absoluten Pfades benutzt. Ich gehe mal durch die Util-Klasse davon aus, das ihr schon ordentlich Klassen benutzt. Warum kommt dann kein Autoloader zum Einsatz? Oder direkt composer?

Da wäre ich mir nicht so sicher, ob PHP da nicht doch was auflöst:

und afaik nutzen require, include, opend_dir, … eben diese Konstante um ihre Arbeit zu machen.

Ich gehe mal durch die Util-Klasse davon aus, das ihr schon ordentlich Klassen benutzt. Warum kommt dann kein Autoloader zum Einsatz? Oder direkt composer?

Autoloader für die imports? Das löst ja nur ein Symptom von vielen, deswegen geht es mir ja um Ursachenbekämpfung. Mit composer hab ich keine Erfahrung und ich bin mir auch nicht sicher, ob das für diese Sachen wirklich notwendig ist. Die „Lösung“ mit symlinks gefällt mir so eigentlich ganz gut, da es halt auch verdammt schnell eingerichtet ist. Nur die Sache mit den falschen Pfaden ist halt echt ein Problem.

Und schon mal FILE ausgeben lassen, wenn du die example.php über den symlink aufrufst? Falls korrekt, dann probiere selbst man ein

require_once(dirname(__FILE__) . '/../xyz.php')

In solcher Hinsicht ist es nicht gut auf die internen Mechanismen Verlass zu geben.

AFAIK ist composer so eine Art Mini-Maven, also nur für die Abhängigkeiten verantwortlich und erstellt dann einen passenden Autoloader für alle Abhängigkeiten und die eigenen Files.


<?php
echo __FILE__."<br />";
echo dirname(__FILE__)."<br />";

require_once(dirname(__FILE__) . '/../xyz.php');
?>

Ausgabe:


C:\xampp\htdocs\sourcen\example.php
C:\xampp\htdocs\sourcen

Warning: require_once(C:\xampp\htdocs\sourcen/../xyz.php): failed to open stream: No such file or directory in C:\xampp\htdocs\sourcen\example.php on line 5

Jep, hier kommt kein symlink vor. Wieder müsste xyz.php im htdocs Ordner liegen. Wie sollte auch PHP auch wissen, das die Datei über den symbolischen Pfad aufgerufen wurde. Den Aufruf über den symbolischen Link erledigt der Apache.

Wie bekommt den PHP die Datei, dass müsste doch so ähnlich ablaufen wie über das terminal:
afair: php C:\xampp\htdocs\example\sample\example.php.
also müsste doch der Pfad nach meinem Verständnis nach zu Anfang stimmen. Danach löst PHP die symbolischen links auf und ich lande bei ../ im falschen Verzeichnis.

Korrigiere mich bitte wenn ich irgendwo einen (Denk)Fehler habe, aber soweit ich das Verstehe versaut mir PHP ganz bewusst meinen Pfad.

Nope, FILE gibt dir immer den physikalischen Ort zurück (getestet mit PHP 5.4.6 und 5.4.17). Eben weil symbolische Links aufgelöst werden. Und das meint, das der symlink in den richtigen Pfad umgewandelt wird.

Wobei das hier helfen sollte: http://blog.eye48.com/post/17154778683/symlinks-php-realpath-lesson-learned

Ok, ja so langsam wirds klar wo mein Denkfehler war. Das einfachste wird wohl sein, ich nutze weiterhin die Utils-Klasse von oben. Sollte ich mal die notwendige Zeit oder Verzweiflung haben, schaue ich mir vllt doch mal den composer an.

Das einfachste ist auf Symlinks zu verzichten. Alternativ könntest du Alias Pfade setzen.

Wieso hast du überhaupt Symlinks? Das eigentliche Projekt sollte am Ende immer in einem physischen Pfad vorliegen und im schlimmsten Fall mit einem Alias arbeiten.