include path resolution
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
aufruf
check
code
datei
file
forum
funktion
http
markup
match
minimum
nennen
not
null
ordner
pfad
problem
url
verzeichnis
zeile
-
Für gewisse Zwecke muss ich alle Pfade bestimmen, die PHP beim includen in Erwägung zieht und in welcher Reihenfolge.
Mein Problem ist, dass ich dazu nur sehr schwammige Dokumentation finde. Auch mein Durchstöbern des PHP SVN hat mich nur zu dem Aufruf der Funktion zend_resolve_path gebracht, dessen Deklaration ich jedoch nicht finden kann (in zend.c wird es als utilities->resolve_path oder so ähnlich definiert, ich kann jedoch nicht herausfinden, wo das utilities herkommt.)
Daher möchte ich mal hier nachfragen, ob sich damit vielleicht jemand auskennt.
Meine bisherigen Erfahrungen.
a) Ordnerstruktur:
subdir
--caller.php
--include.php (nennen wirs mal INC_A)
executer.php
include.php (nennen wirs mal INC_B)
Wenn executer.php nun caller.php (über include) einbindet und caller.php dann ein "include 'include.php'" oder "include './include.php'" aufruft, kommt beide male INC_B rein.
b) Ordnerstruktur:
subdir
--caller.php
--include.php (nennen wirs mal INC_A)
executer.php
(diesmal also ohne INC_B)
Wenn executer.php nun caller.php (über include) einbindet und caller.php dann ein "include 'include.php'" aufruft, wird INC_A eingebunden, mit ./include.php bekomme ich file does not exist warnings.
Include Path zu dem Zeitpunkt, sei als '.' anzusehen ;) (Wenn ich den gleich 'hallo-world' gesetzt habe, dann lies sich Beobachtung b) auch weiterhin feststellen.)
Also, jemand ne Idee, wie das ganze abläuft? -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Hm, ich weiß nicht, ob ich Dich jetzt richtig verstanden habe. Aber hier mal eine Beschreibung, wie ich es bisher herausgefunden habe.
1.) PHP versucht bei relativen Pfadangaben immer die Datei in einem Pfad zu finden, der relativ zum Pfad des geöffneten Haupt-Skripts ist.
Rufst Du also executer.php auf und inkludierst caller.php, werden alle in caller.php stehenden include-Pfadangaben relativ zum Pfad von executer.php gesehen. Somit erklärt sich Deine Beobachtung a)
2.) Existiert diese Datei nicht, versucht PHP die Datei in den include-path Verzeichnissen zu finden. Existiert es auch dort nicht, scheitert der gesamte Vorgang. Wieso jetzt aber INC_A damit eingebunden wird, kann ich auch nicht so recht nachvollziehen. Evtl. wird erkannt, dass sich zwar in der Ebene von executer.php kein "include.php" File (also INC_B) befindet, dafür aber wohl eines im Verzeichnis von caller.php und deshalb wird dieses eingebunden.
3.) Willst Du, dass INC_A durch caller.php eingebunden wird, obwohl Du dieses selbst inkludiert hast, solltest Du absolute Pfadangaben wählen, damit auch klar wird, welches include.php gemeint ist.
Edit: Typo
Beitrag zuletzt geändert: 15.4.2010 17:39:43 von rnitsche -
Mein Problem ist gerade wie b) zustande kommt. Es ist zwar irgendwie "logisch", dass die Datei eingebunden wird, im Manual kann ich jedoch nichts dazu finden. Und ich brauche für meinen Anwendungszweck wirklich eine zuverlässige Angabe.
@3. Wollen tue ich an sich gar nichts. Ich möchte nur wissen, was PHP alles nehmen könnte. (Wofür genau dauert lange zu erklären)
€dit: Habe gerade http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/main/fopen_wrappers.c?view=markup gefunden, Zeile 510 php_resolve_path. Hoffe das bringt mich weiter :D
Beitrag zuletzt geändert: 15.4.2010 17:56:21 von nikic -
Zitat von php.net:
Files are included based on the file path given or, if none is given, the include_path specified. The include() construct will emit a warning if it cannot find a file; this is different behavior from require(), which will emit a fatal error.
Somit dürfte INC_A niemals eingebunden werden.
Hast Du evtl. den include_path verändert? Unter Linux gilt eine Pfadangabe /home/nikic// (also mit 2 slashes) als "Dieser Ordner und alle Unterordner". Wenn Du also in deinem include-path z.B. ".//:/usr/php/tmp" stehen hast, so gibt der Doppelslash dem Parser die Anweisung, die entsprechende Datei zunächst im aktuellen Verzeichnis (also ".") zu suchen. Ist sie dort nicht, gibt der include_path vor, in alle Unterverzeichnissen zu suchen und landet dann bei der INC_A.
Edit: Hab Deinen edit-Zusatz nicht gesehen.
Beitrag zuletzt geändert: 15.4.2010 18:03:22 von rnitsche -
Nope, include_path war auf '.' gesetzt oder viel eher auf '.;irgendnen-pear-dir-wo-die-datei-sicher-net-ist'. Danach hab ich den include_path auf 'hallo-world' gesetzt, um wirklich alle Fehlerquellen von dieser Seite aus zu schließen, aber es kam zum selben Ergebnis :(
-
Mal ein ganz doofer Einwand, wenn man sicher gehen will, dass immer eine bestimmte Datei eingebunden wird, dann sollte man den kompletten root-path mit eingeben. Wie kann man das machen?
In der Hauptdatei wird eine Konstante definiert, in der der Pfad zu dieser Hauptdatei gespeichert ist und von da an bindet man seine zusätzlichen Dateien immer relativ zu diesem Pfad ein.
Ein Beispiel,
wir haben folgende Verzeichnisstruktur:
So und dann die beiden wichtigen Dateien:root/ - includeDir/ - + caller.php - + include.php + include.php + executer.php
executer.php<?php define( "ROOT_PATH", dirname(__FILES__) . '/' ); include( ROOT_PATH . 'includeDir/caller.php' ) ?>
und caller.php<?php # eine Datei include( ROOT_PATH . 'include.php' ); # dann noch eine, mit dem selben Namen in einem anderen Verzeichnis include( ROOT_PATH . 'includeDir/include.php' ); ?>
Eigentlich finde ich diesen Weg am sinnvollsten, da man so nicht auf's Glatteis gerät und die Übersicht verliert. -
@nemoinho: So würde ich das auch machen. Ich möchte aber nicht selbst etwas einbinden, sondern herausfinden, was eingebunden werden könnte.
So, hier die Auflösung, für alle, die das interessieren sollte.
In http://svn.php.net/viewvc/php/php-src/trunk/main/fopen_wrappers.c?view=markup in Zeile 510 findet sich die Funktion
PHPAPI char *php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC)
Beim Aufruf dieser läuft folgendes ab.
Einige Variablen definiteren, die anschließend genutzt werden.
512 char resolved_path[MAXPATHLEN]; 513 char trypath[MAXPATHLEN]; 514 const char *ptr, *end, *p; 515 char *actual_path; 516 php_stream_wrapper *wrapper;
Prüfen, dass ein Filename besteht.
if (!filename) { 519 return NULL; 520 }
Überprüfen, ob es sich um einen Stream-Wrapper handelt, wenn ja, nicht auflösen (ausgenommen file://-Wrapper):
/* Don't resolve paths which contain protocol (except of file://) */ 523 for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); 524 if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) { 525 wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); 526 if (wrapper == &php_plain_files_wrapper) { 527 if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { 528 return estrdup(resolved_path); 529 } 530 } 531 return NULL; 532 }
Checken ob es ein relativer Pfad
534 if ((*filename == '.' && 535 (IS_SLASH(filename[1]) || 536 ((filename[1] == '.') && IS_SLASH(filename[2])))) ||
oder ein absoluter Pfad ist
537 IS_ABSOLUTE_PATH(filename, filename_length) ||
oder ob kein include_path besteht
538 !path || 539 !*path) {
Wenn eines dieser zutrifft, dann direkt auflösen:
540 if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { 541 return estrdup(resolved_path); 542 } else { 543 return NULL; 544 }
(Die Funktion tsrm_realpath findet sich übrigens in http://svn.php.net/viewvc/php/php-src/trunk/TSRM/tsrm_virtual_cwd.c?view=markup.)
Anschließend wird von Zeile 547 bis Zeile 600 durch den include_path iteriert und alles getestet (dauert zu lange das im Detail zu erläutern.)
Und jetzt kommt der Lümmel:
602 /* check in calling scripts' current working directory as a fall back case 603 */
PHP greift im Zweifelsfall dann nochmal auf die aufrufende Datei zurück:
Zuerst holt es sich die aufrufende Datei:
605 char *exec_fname = zend_get_executed_filename(TSRMLS_C); 606 int exec_fname_length = strlen(exec_fname);
Verkleinert dann exec_fname_length, sodass nur das Direcotory, nicht der ganze Pfad übrig bleibt:
608 while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
Ein paar Prüfungen:
609 if (exec_fname && exec_fname[0] != '[' && 610 exec_fname_length > 0 && 611 exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
Directory der aufrufenden Datei kopieren:
612 memcpy(trypath, exec_fname, exec_fname_length + 1);
und den filename, der php_resolve_path übergeben wurde anhängen:
613 memcpy(trypath+exec_fname_length + 1, filename, filename_length+1); 614 actual_path = trypath;
Nach einem Check auf Stream-Wrapper, wird dann der realpath bestimmt:
634 if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { 635 return estrdup(resolved_path); 636 }
Und zu guter letzt, wenn echt gar nichts geht
return NULL
Und gerade dieser letzte "check in calling scripts' current working directory as a fall back case" Abschnitt führt zu b) -
Ich hab für das ganze eine Klasse geschrieben, die versucht möglichst nah an das PHP-Verhalten heranzukommen. Ich publiziere das mal hier, falls das noch jemand braucht.
<?php // This class' job is to simulate PHP's inclusion behaviour // I tried to base it on PHP's implementation, but there are still differences, // e.g. this class does not resolve symlinks class Prephp_Path { private static $slash; private static $antiSlash; private static $win; // initializes slashes and OS public static function init() { self::$slash = DIRECTORY_SEPARATOR; self::$antiSlash = self::$slash=='/'?'\\':'/'; self::$win = PHP_OS == 'WINNT' || PHP_OS == 'WIN32'; } // expects: no restrictions // returns all possible paths, where PHP will look whilst including public static function possiblePaths($filename, $caller, $executer, $include_path = null) { $executer = dirname($executer); $caller = dirname($caller); if ($include_path === null) { $include_path = get_include_path(); } // stream wrapper if (self::isStreamWrapper($filename)) { return array($filename); } if (self::isRelative($filename) || self::isAbsolute($filename)) { return array(self::normalize($filename, $executer)); } $paths = array(); if ($include_path != '') { $include_paths = explode(PATH_SEPARATOR, $include_path); foreach ($include_paths as $path) { try { if (self::isRelative($path)) { $path = $executer . self::$slash . $path; } $paths[] = self::normalize($filename, $path); } catch(InvalidArgumentException $e) {} } } $paths[] = self::normalize($caller . self::$slash . $filename); return $paths; } // expects: no restrictions public static function normalize($filename, $cwd = '') { // first check for stream wrapper if (self::isStreamWrapper($filename)) { return $filename; // no further processing (should one do something with file://?) } // all further functions require normalized slashes; $filename = self::normalizeSlashes($filename); $cwd = self::normalizeSlashes($cwd); if (!self::isAbsolute($filename)) { if (!self::isAbsolute($cwd) && !self::isStreamWrapper($cwd)) { throw new InvalidArgumentException('cwd is not absolute, not a stream wrapper or not specified!'); } $filename = $cwd . self::$slash . $filename; } // now resolve ./ and ../ $filename = array_reduce(explode(self::$slash, $filename), array(__CLASS__, 'normalizor'), 0); // strip multiple slashes $filename = self::stripDoubleSlashes($filename); return $filename; } // internal! // used for array_reduce in self::normalize() protected static function normalizor($current, $next) { if ($next == '.') { return $current; } if ($next == '..') { return dirname($current); } if ($current === 0) { // initial return $next; } return $current . self::$slash . $next; } // normalizes slashes to self::$slash // expects: non-wrapper filename public static function normalizeSlashes($filename) { return str_replace(self::$antiSlash, self::$slash, $filename); } // removes double slashes (///) // expects: slash-normalized non-wrapper filename public static function stripDoubleSlashes($filename) { return (self::isUNC($filename)?'\\':'') // do not strip \\ at beginning (UNC path) . preg_replace('#'.preg_quote(self::$slash).'{2,}#', self::$slash, $filename); } // chechs if is relative path // ./ and ../ // (X:./ would be relative too, actually, but this is PHP's implementation) // expects: slash-normalized filename public static function isRelative($filename) { $l = strlen($filename); return $l > 1 // minimum two chars, to be relative && $filename[0] == '.' && (self::isSlash($filename[1]) || ($l > 2 && $filename[1] == '.' && self::isSlash($filename[2]))); } // checks if is absolute path (X: and UNC for win, / otherwise) // expects: slash-normalized filename public static function isAbsolute($filename) { $l = strlen($filename); // Windows if (self::$win) { return $l > 1 // minimum of two chars && ( (self::isSlash($filename[0]) && self::isSlash($filename[1])) // UNC path || (ctype_alpha($filename[0]) && $filename[1]==':') // X: path (actually, this may be relative, but PHP implements it this way) ); } // Unix return $l && self::isSlash($filename[0]); } // check if is UNC-path (win only) // expects: slash-normalized filename public static function isUNC($filename) { return self::$win && strlen($filename) > 1 && self::isSlash($filename[0]) && self::isSlash($filename[1]); } // checks if is stream wrapper // important: does not check, whether the stream wrapper actually exists // expects: UNNORMALIZED slashes (file:\\ is not recognized, for sure) public static function isStreamWrapper($filename, &$addinf = 'no_addinf') { if (preg_match('#^([-+.a-zA-Z0-9]+)://(.*)$#', $filename, $matches)) { if ($addinf != 'no_addinf') { $addinf = array($matches[1], $matches[2]); } return true; } return false; } // internal! // checks if $char is self::$slash // expects: normalized slash protected static function isSlash($char) { return $char == self::$slash; } } Prephp_Path::init(); ?>
-
Wenn ich das alles richtig verstanden habe, ohne alles gelesen zu haben, dann hast du das Problem, welches ich vor Kurzem auch hatte:
Datei gateway.php inkludiert die Datei /forum/global.php, welche wiederrum init.php im Ordner /ordner/ aufruft. Wenn ich nun gateway.php aufrufe, erhalte ich die Meldung, dass config.php nicht gefunden werden kann.
Entspricht das deinem Problem?
Ich konnte es bei mir so lösen:
$root = $_SERVER['DOCUMENT_ROOT']; $curdir = getcwd (); chdir("$root/forum/"); require("$root/forum/global.php"); chdir ($curdir);
Könnte auch sein, dass ich euch jetzt von einem komplett anderen Problem erzähle, aber mir sind das hier grad zu viele Buchstaben auf einmal :D -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage