Which directories does PHP check when including a relative path with include()?
Asked Answered
A

3

9

It's very odd,has anyone ever sum up with a conclusion yet?

Sometimes it checks the directory of the included file,too.

But sometimes not.

D:\test\1.php

<?php

include('sub\2.php');

D:\test\2.php

<?php

include('3.php');

Where 3.php is in the same dir as 2.php.

The above works,but why?The current directory should be D:\test,but it can still find 3.php,which is in D:\test\sub

More story(final)

About a year ago I met this problem,and then I ended up fixed it with the hardcoding like below:

Common.php:

if (file_exists("../../../Common/PHP/Config.inc"))
    include('../../../Common/PHP/Config.inc');

if (file_exists("../../Common/PHP/Config.inc"))
    include('../../Common/PHP/Config.inc');

if (file_exists("../Common/PHP/Config.inc"))
    include('../Common/PHP/Config.inc');

if (file_exists("Common/PHP/Config.inc"))
    include('Common/PHP/Config.inc');

Where Config.inc is in the same directory as Common.php

Allveta answered 13/3, 2010 at 11:37 Comment(4)
Good question! I can confirm this on Windows and Linux. I have no idea why this is.Phylogeny
Regarding your last example, if "Config.inc" is in the same directory as "Common.php" then this could be simplified to include(dirname(__FILE__).'/Config.inc'); - this will always work, regardless of the include_path and which file "Common.php" is included into. If there is no chance "Config.inc" could be found in the include_path (in which the current directory is often included) then you could simply call include 'Config.inc';, although this is possibly less efficient since the include_path is first searched (which fails).Psychochemical
In your first example, I assume "D:\test\2.php" should be "D:\test\sub\2.php"? (Otherwise include('sub\2.php'); would never work.)Psychochemical
I just experienced what you describe. I've always included files without the path, inside my included file (where both files exist in the same sub directory) and I have this working on multiple servers. On Friday, on one server, this suddenly stopped working. I now have to specify the path in the include, or it won't find it. I have no idea why. Exact same versions of PHP on all servers. If you ever figured this out, I'd love to get an update on your story.Hyperon
I
2

If you take a look at the source code for php in main/fopen_wrappers.c you will find

/* check in calling scripts' current working directory as a fall back case
     */
    if (zend_is_executing(TSRMLS_C)) {
        char *exec_fname = zend_get_executed_filename(TSRMLS_C);
        int exec_fname_length = strlen(exec_fname);

        while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
        if (exec_fname && exec_fname[0] != '[' &&
            exec_fname_length > 0 &&
            exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
            memcpy(trypath, exec_fname, exec_fname_length + 1);
            memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
            actual_path = trypath;

This seems to be executed unconditionally and therefore will always make a file in the same path as the including/file-opening script accessible ...as the last choice after all possibilities specified in include_path. And only if you do not define a relative or absolute path in the include().

Ivette answered 13/3, 2010 at 13:38 Comment(15)
I dont seem to understand this code,are there some clues why sometimes it doesn't check the directory of the included file?Allveta
For 3.php it first tests all locations given in the include_path. A relative path given there, e.g. include_path=.;..;lalala would be relative to the current working directory (which is not changed by an include()). Only the last (additional) option is to look in the same directory as the including file is in. It "takes" the first 3.php it can find. Is that your problem: php grabs another 3.php as you'd expect? Your other questions seem to indicate that...Ivette
My problem is sometimes it won't find 3.php,but I can't reproduce it easily.But it's in the same directory as the including file is in.Maybe when the include is in a function/method,I guess...Allveta
So, for clarification: It doesn't find any 3.php in that case (not simply the "wrong" one but none at all). You can't reproduce it easily... hm. Does it happen on different servers/versions/configurations? Or is it like a heisenbug that happens on the same machine with the same code without touching anything that seems to be related?Ivette
Yes.It happens on different servers,I don't think it's a heisenbugAllveta
And you're sure the problem really boils down to the example you've provided in the question? (Sorry for me nagging, but I have the feeling there's something missing in the example and the question "The above works,but why?" as it is right now is answered ;-) )Ivette
@Ivette ,this answers half the question(why it works),did you miss the But sometimes not. part in my post?:-)Allveta
Yes I did ;-) But since I have no further clue maybe there's some information you can add. E.g. "It happens on different servers" is something that should make you hit the "edit" button of your original question. Maybe you have noticed some/any pattern. The php version, something in the setting of include_path on the servers, something remarkable about the "configure command" in the output of phpoinfo() on the different servers ...anything ;-) Because as you can see from all the answers so far this behaviour of php is not something too obvious.Ivette
I've provided the whole story(imo) here:#2438856Allveta
Ok, that's the story of the hack you want add to your code. I meant the story of how you discovered and explored the underlying problem. It may be just me but I've never seen code before that had to resort to debug_backtrace() to get an include right ;-) Right now you're working on a hack around the problem. And as hacks go they come back at you at the least favorable time in the least favorable manner.Ivette
Yes,glad you've fully understood my problem(which can't be reproduced with ease:():-)Finally I manually changed the cwd by chdir() as a workaround,which is much safer/efficient(imo)..Allveta
Just one last question... which versions of php are involved? There have been some patches (and reverts) in the 4.3/5.0 branch that might affect whether php is executing the code I quoted above at all or not.Ivette
@Ivette ,PHP 5.3.0.But I don't think it's related with OOP,because I noticed this before using classes,but I didn't stop to think about the reason at that time:-)Allveta
Each and every server involved is php 5.3? Ok, then I don't have a clue (and no I didn't mean oop, but zend_stream_open_function ;-))Ivette
+1 never knew that php would use the directory of the file that did the include as a last resort. I always thought the cwd and include_path were the only choices.Gamosepalous
P
1

It checks in the current path, and the directories listed in include_path.

You can run a phpinfo() to see your include path.

Phylogeny answered 13/3, 2010 at 11:41 Comment(4)
This doesn't explain the problem I described:(Allveta
@user as far as I know, that's all there is to it. Please show some examples of when files get loaded, and when they don't.Phylogeny
Will getcwd() change during a request if we don't explicitly change it(like chdir()).I'll give an example soon.Allveta
If by "current path" you mean the current working directory then it only checks the CWD if it's included as part of the include_path (which it is by default). However, if by "current path" you mean the directory that contains the script (ie. dirname(__FILE__) - which has the include statement and is not necessarily the same as the CWD) then this is only checked after it fails to find it in the include_path.Psychochemical
B
1

Sometimes directory of the included file being current working directory and sometimes not
Current directory can be checked with getcwd()

Bohlen answered 13/3, 2010 at 11:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.