How does include path resolution work in require_once?
Asked Answered
G

5

20

I was writing an web app in PHP, when I encountered a strange situation. To illustrate my problem, consider a web app of this structure:

/
    index.php
    f1/
        f1.php
    f2/
        f2.php

Contents of these files:

index.php:

<?php require_once("f1/f1.php"); ?>

f1.php:

<?php require_once("../f2/f2.php"); ?>

f2.php: blank

now when I try to open index.php in my browser I get this error:

Warning: require_once(../f2/f2.php) [function.require-once]: 
failed to open stream: No such file or directory in /var/www/reqtest/f1/f1.php on line 2
Fatal error: require_once() [function.require]: 
Failed opening required '../f2/f2.php' (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/reqtest/f1/f1.php on line 2

Is there something obvious I'm missing? how do include paths work in PHP?


Before I asked this question, I attempted to experiment and find out. I set up another test, like so:

/
    index.php
    f1/
        f1.php
        f2.php

index.php:

<?php require_once("f1/f1.php"); ?>

f1.php:

<?php require_once("f2.php"); ?>

f2.php: blank

To my surprise (and utter confusion), this worked out fine!

So, what is the secret behind the path resolution?

PS I saw this question, but it still does not answer the second case that I've stated here.

Goodard answered 29/12, 2009 at 9:59 Comment(4)
I've already circumvented this problem (using dirname). What I want to know is why the second case does not fail. Is it a bug or a feature?Goodard
Edited my answer to cover the second example.Dippy
I can't find the manual page that documents the successful call to require_once('f2.php') from f1.php. Docs say that include_path is ignored when no path info is provided (whatever, removing '.' from include_path has no effect) and getcwd() shows that working directory is the same all around the include chain. Seriously, it looks like an undocumented feature.Maudemaudie
Helpful article: cjhaas.com/2019/05/21/php-include-path-surprisesCullan
D
14

If you include another file, the working directory remains where the including file is.

Your examples are working as intended.

Edit: The second example works because . (actual directory) is in your include path (see your error message).

Edit2: In your second example, the key point of your interest is this line:

<?php require_once("f2.php"); ?>

At first it will look in the current working dir (/var/www/req_path_test), but does not find f2.php.

As fallback, it will try to find f2.php in your include_path ('.:/usr/share/php:/usr/share/pear'), starting with '.' (which is relative to the actual file, not the including one).

So './f2.php' works and the require does not fail.

Dippy answered 29/12, 2009 at 10:23 Comment(4)
I tried echo:ing getcwd() in index.php and f1.php. Output from both places is /var/www/req_path_test so "." being in the include path would probably mean "/var/www/req_path_test" is in the include path.Goodard
and in support of this fact, I tried require_once("f2/f2.php"). It worked when I open up index.php, but not when I open f1/f1.php.Goodard
hmm, that seems to be the most appropriate explanation.. can you please cite any sources?Goodard
I think this works counter to other languages. For that reason, I would call this a design flaw. The author of this comment is demonstrating what other languages/systems would do.Victoriavictorian
S
4

When you open index.php, working dir is set to the folder this file resides in. And inside insluded f1.php this working dir does not change.

You can include files by using their absolute paths, relative to the current included file like this:

require_once(dirname(__FILE__).'/../../test/file.php')

But better consider using an autoloader if these files contain classes.

Starwort answered 29/12, 2009 at 10:15 Comment(1)
Try to add 'echo getcwd();' to your scripts to see, how and if current working directory changes. php.net/manual/en/function.getcwd.php - comments here says, that it's behavior changed from PHP4 to PHP5 for example.Starwort
U
0

Normaly in you old structure

<?php require_once("f2/f2.php"); ?>

instead of

<?php require_once("../f2/f2.php"); ?>

should work. As far as i know php takes the paths from the initial script

Urbina answered 29/12, 2009 at 10:4 Comment(1)
I know. What I really want to know is why the second case is not failing. Its not a problem, its an itch :)Goodard
P
0

It sounds like your server has the open_basedir setting enabled in the PHP configuration. This makes it impossible to include (and open) files in folders above your in the directory structur (i.e., you can't use ../ to go up in the folder structure).

Paragrapher answered 29/12, 2009 at 10:21 Comment(1)
in my server, open_basedir setting has "no value". What would that mean?Goodard
I
0

From the PHP Docs PHP include

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.

If the file path is not given then i.e require_once("f2.php");

1st. The include_path is checked

2nd. The calling scripts own directory is checked

3rd. Finally the current working directory is checked

If file not found then PHP throws warning on file include & fatal error on require

If a path is defined — whether absolute (starting with a drive letter or \ on Windows, or / on Unix/Linux systems) or relative to the current directory (starting with . or ..) — the include_path will be ignored altogether. For example, if a filename begins with ../, the parser will look in the parent directory to find the requested file.

If you include/require your file beginning with . or .. or ./ then PHP's parser will look in the parent directory which is the current working directory i.e require_once("../f2/f2.php"), php will check at the root directory as the calling script index.php is in that directory.

Now You have not defined any include path in your PHP script thus it always falls back to the calling script and then into the current working directory.

// Check your default include path, most likely to be C:\xampp\php\PEAR
echo get_include_path();

// To set include path
set_include_path ( string $new_include_path ) : string

The Current Working Directory is derived from your main calling script index.php.

// The Current Working Directory can be checked
echo getcwd();

In the first Example where the required file "../f2/f2.php" is from f1.php

You code does not work because -

  1. The specified path is ignored by PHP as your filename begins with ../
  2. f1/ the calling script's own directory is ignored as well.
  3. The parser directory looks into the parent directory to find the requested file. The current working directory is root directory, this is from where you have initiated the working script index.php. The file is not located at this directory, wrong path given.

Thus you get the Fatal Error

In the Second example you have changed the directory & from f1.php you require_once("f2.php").

Your code works because -

  1. This time you require("f2.php") no leading ../ or ./ This time PHP checks the include_path but does find it there, as you haven't defined it and the file does not reside in the default preset include_path.
  2. This time the calling script f1.php's directory is f1/. and you require file ("f2.php") is located at this directory. PHP This time checks the file in this directory and finds it.
  3. PHP does not have to check the working directory as the file was found.

Thus Your Code Works Fine!

Indeed answered 27/2, 2019 at 2:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.