kostenloser Webspace werbefrei: lima-city


include path resolution

lima-cityForumProgrammiersprachenPHP, MySQL & .htaccess

  1. Autor dieses Themas

    nikic

    Kostenloser Webspace von nikic, auf Homepage erstellen warten

    nikic hat kostenlosen Webspace.

    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?
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

  3. 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
  4. Autor dieses Themas

    nikic

    Kostenloser Webspace von nikic, auf Homepage erstellen warten

    nikic hat kostenlosen Webspace.

    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
  5. 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
  6. Autor dieses Themas

    nikic

    Kostenloser Webspace von nikic, auf Homepage erstellen warten

    nikic hat kostenlosen Webspace.

    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 :(
  7. 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:
    root/
    - includeDir/
    - + caller.php
    - + include.php
    + include.php
    + executer.php
    So und dann die beiden wichtigen Dateien:
    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.
  8. Autor dieses Themas

    nikic

    Kostenloser Webspace von nikic, auf Homepage erstellen warten

    nikic hat kostenlosen Webspace.

    @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)
  9. Autor dieses Themas

    nikic

    Kostenloser Webspace von nikic, auf Homepage erstellen warten

    nikic hat kostenlosen Webspace.

    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();
    ?>
  10. 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
  11. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!